create-filament 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/README.md +417 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +937 -0
- package/package.json +38 -0
- package/templates/api/src/config/app.ts +17 -0
- package/templates/api/src/handlers/errors.ts +32 -0
- package/templates/api/src/handlers/finalizers.ts +27 -0
- package/templates/api/src/handlers/transforms.ts +18 -0
- package/templates/api/src/index.ts +45 -0
- package/templates/api/src/meta/defaults.ts +28 -0
- package/templates/api/src/meta/index.ts +50 -0
- package/templates/api/src/middleware/index.ts +36 -0
- package/templates/api/src/routes/index.ts +26 -0
- package/templates/api/tests/integration/example.test.ts +20 -0
- package/templates/full/src/config/app.ts +17 -0
- package/templates/full/src/handlers/errors.ts +32 -0
- package/templates/full/src/handlers/finalizers.ts +27 -0
- package/templates/full/src/handlers/transforms.ts +18 -0
- package/templates/full/src/index.ts +45 -0
- package/templates/full/src/meta/defaults.ts +28 -0
- package/templates/full/src/meta/index.ts +31 -0
- package/templates/full/src/middleware/index.ts +36 -0
- package/templates/full/src/routes/index.ts +26 -0
- package/templates/full/tests/integration/example.test.ts +20 -0
- package/templates/minimal/src/config/app.ts +17 -0
- package/templates/minimal/src/handlers/errors.ts +32 -0
- package/templates/minimal/src/handlers/finalizers.ts +27 -0
- package/templates/minimal/src/handlers/transforms.ts +18 -0
- package/templates/minimal/src/index.ts +45 -0
- package/templates/minimal/src/meta/defaults.ts +28 -0
- package/templates/minimal/src/meta/index.ts +31 -0
- package/templates/minimal/src/middleware/index.ts +36 -0
- package/templates/minimal/src/routes/index.ts +26 -0
- package/templates/minimal/tests/integration/example.test.ts +20 -0
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-filament",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Scaffold a new Filament application",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-filament": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"filament",
|
|
20
|
+
"scaffold",
|
|
21
|
+
"typescript",
|
|
22
|
+
"api",
|
|
23
|
+
"framework"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "ISC",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"prompts": "^2.4.2",
|
|
29
|
+
"chalk": "^5.3.0",
|
|
30
|
+
"ora": "^8.0.1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"@types/prompts": "^2.4.9",
|
|
35
|
+
"typescript": "^5.0.0",
|
|
36
|
+
"test-battery": "^3.2.1"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const config = {
|
|
6
|
+
port: parseInt(process.env.PORT || '3000', 10),
|
|
7
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
8
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate required environment variables
|
|
13
|
+
*/
|
|
14
|
+
export function validateConfig() {
|
|
15
|
+
// Add validation for required env vars here
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import pino from 'pino';
|
|
4
|
+
|
|
5
|
+
const logger = pino({
|
|
6
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Register error handlers
|
|
11
|
+
*/
|
|
12
|
+
export function registerErrorHandlers(app: Application<AppMeta>) {
|
|
13
|
+
app.onError(async (err, req, res) => {
|
|
14
|
+
const level = req.endpointMeta.logging.level;
|
|
15
|
+
|
|
16
|
+
if (level === 'debug' || level === 'error') {
|
|
17
|
+
logger.error({
|
|
18
|
+
error: err.message,
|
|
19
|
+
stack: err.stack,
|
|
20
|
+
method: req.method,
|
|
21
|
+
path: req.path,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!res.headersSent) {
|
|
26
|
+
res.status(500).json({
|
|
27
|
+
error: 'Internal Server Error',
|
|
28
|
+
message: process.env.NODE_ENV === 'development' ? err.message : undefined,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import pino from 'pino';
|
|
4
|
+
|
|
5
|
+
const logger = pino({
|
|
6
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Register finalizers (always run)
|
|
11
|
+
*/
|
|
12
|
+
export function registerFinalizers(app: Application<AppMeta>) {
|
|
13
|
+
app.onFinalize(async (req, res) => {
|
|
14
|
+
const duration = Date.now() - (req._startTime || Date.now());
|
|
15
|
+
const level = req.endpointMeta.logging.level;
|
|
16
|
+
|
|
17
|
+
if (level === 'debug' || level === 'info') {
|
|
18
|
+
logger.info({
|
|
19
|
+
method: req.method,
|
|
20
|
+
path: req.path,
|
|
21
|
+
status: res.statusCode,
|
|
22
|
+
duration,
|
|
23
|
+
event: 'request.complete',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register response transformers
|
|
6
|
+
*/
|
|
7
|
+
export function registerTransformers(app: Application<AppMeta>) {
|
|
8
|
+
// Add custom headers based on tags
|
|
9
|
+
app.onTransform(async (req, res) => {
|
|
10
|
+
const tags = req.endpointMeta.tags;
|
|
11
|
+
|
|
12
|
+
if (tags.includes('api')) {
|
|
13
|
+
res.setHeader('X-API-Version', '1.0');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Add more transformations here
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createApp } from 'filamentjs';
|
|
2
|
+
import { defaultMeta } from './meta/defaults.js';
|
|
3
|
+
import { config, validateConfig } from './config/app.js';
|
|
4
|
+
import { registerMiddleware } from './middleware/index.js';
|
|
5
|
+
import { registerRoutes } from './routes/index.js';
|
|
6
|
+
import { registerErrorHandlers } from './handlers/errors.js';
|
|
7
|
+
import { registerTransformers } from './handlers/transforms.js';
|
|
8
|
+
import { registerFinalizers } from './handlers/finalizers.js';
|
|
9
|
+
|
|
10
|
+
// Validate configuration
|
|
11
|
+
validateConfig();
|
|
12
|
+
|
|
13
|
+
// Create Filament application
|
|
14
|
+
const app = createApp(defaultMeta);
|
|
15
|
+
|
|
16
|
+
// Register middleware
|
|
17
|
+
registerMiddleware(app);
|
|
18
|
+
|
|
19
|
+
// Register routes
|
|
20
|
+
registerRoutes(app);
|
|
21
|
+
|
|
22
|
+
// Register post-request handlers
|
|
23
|
+
registerErrorHandlers(app);
|
|
24
|
+
registerTransformers(app);
|
|
25
|
+
registerFinalizers(app);
|
|
26
|
+
|
|
27
|
+
// Graceful shutdown
|
|
28
|
+
process.on('SIGTERM', async () => {
|
|
29
|
+
console.log('SIGTERM received, shutting down gracefully...');
|
|
30
|
+
await app.close();
|
|
31
|
+
process.exit(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
process.on('SIGINT', async () => {
|
|
35
|
+
console.log('SIGINT received, shutting down gracefully...');
|
|
36
|
+
await app.close();
|
|
37
|
+
process.exit(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Start server
|
|
41
|
+
app.listen(config.port, () => {
|
|
42
|
+
console.log(`🔥 Filament server running on http://localhost:${config.port}`);
|
|
43
|
+
console.log(` Environment: ${config.nodeEnv}`);
|
|
44
|
+
console.log(` Log level: ${config.logLevel}`);
|
|
45
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AppMeta } from './index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default metadata for all endpoints
|
|
5
|
+
*
|
|
6
|
+
* Individual endpoints can override these values with Partial<AppMeta>
|
|
7
|
+
*/
|
|
8
|
+
export const defaultMeta: AppMeta = {
|
|
9
|
+
requiresAuth: false,
|
|
10
|
+
rateLimit: 100,
|
|
11
|
+
logging: {
|
|
12
|
+
level: 'info',
|
|
13
|
+
},
|
|
14
|
+
tags: [],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Common metadata presets for convenience
|
|
19
|
+
*/
|
|
20
|
+
export const PUBLIC: Partial<AppMeta> = {
|
|
21
|
+
requiresAuth: false,
|
|
22
|
+
rateLimit: 100,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const AUTHENTICATED: Partial<AppMeta> = {
|
|
26
|
+
requiresAuth: true,
|
|
27
|
+
rateLimit: 50,
|
|
28
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FrameworkMeta } from 'filamentjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Application metadata interface
|
|
5
|
+
*
|
|
6
|
+
* Extend this interface to add your own metadata properties.
|
|
7
|
+
* Middleware can inspect req.endpointMeta to make decisions.
|
|
8
|
+
*/
|
|
9
|
+
export interface AppMeta extends FrameworkMeta {
|
|
10
|
+
/**
|
|
11
|
+
* Whether this endpoint requires authentication
|
|
12
|
+
*/
|
|
13
|
+
requiresAuth: boolean;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Required role for this endpoint
|
|
17
|
+
*/
|
|
18
|
+
role?: 'admin' | 'user';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether this endpoint requires a session
|
|
22
|
+
*/
|
|
23
|
+
requiresSession: boolean;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Rate limit for this endpoint (requests per minute)
|
|
27
|
+
*/
|
|
28
|
+
rateLimit: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Logging configuration
|
|
32
|
+
*/
|
|
33
|
+
logging: {
|
|
34
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Tags for categorizing endpoints
|
|
39
|
+
*/
|
|
40
|
+
tags: string[];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* OpenAPI documentation
|
|
44
|
+
*/
|
|
45
|
+
openapi?: {
|
|
46
|
+
summary: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
tags?: string[];
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import pino from 'pino';
|
|
4
|
+
|
|
5
|
+
const logger = pino({
|
|
6
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
7
|
+
transport: {
|
|
8
|
+
target: 'pino-pretty',
|
|
9
|
+
options: {
|
|
10
|
+
colorize: true,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Register all middleware
|
|
17
|
+
*/
|
|
18
|
+
export function registerMiddleware(app: Application<AppMeta>) {
|
|
19
|
+
// Request logging middleware
|
|
20
|
+
app.use(async (req, res, next) => {
|
|
21
|
+
const level = req.endpointMeta.logging.level;
|
|
22
|
+
|
|
23
|
+
if (level === 'debug' || level === 'info') {
|
|
24
|
+
logger.info({
|
|
25
|
+
method: req.method,
|
|
26
|
+
path: req.path,
|
|
27
|
+
event: 'request.start',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await next();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Add more middleware here
|
|
35
|
+
// Example: Authentication, rate limiting, etc.
|
|
36
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import { PUBLIC } from '../meta/defaults.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Register all routes
|
|
7
|
+
*/
|
|
8
|
+
export function registerRoutes(app: Application<AppMeta>) {
|
|
9
|
+
// Health check endpoint
|
|
10
|
+
app.get('/health', PUBLIC, async (req, res) => {
|
|
11
|
+
res.json({
|
|
12
|
+
status: 'healthy',
|
|
13
|
+
timestamp: new Date().toISOString(),
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Example endpoint
|
|
18
|
+
app.get('/', PUBLIC, async (req, res) => {
|
|
19
|
+
res.json({
|
|
20
|
+
message: 'Welcome to your Filament API',
|
|
21
|
+
endpoints: {
|
|
22
|
+
health: 'GET /health',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { suite, it, before, after } from 'node:test';
|
|
2
|
+
import { TestBattery } from 'test-battery';
|
|
3
|
+
|
|
4
|
+
suite('Example Test', () => {
|
|
5
|
+
before(() => {
|
|
6
|
+
// Setup code here
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
after(() => {
|
|
10
|
+
// Cleanup code here
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
TestBattery.test('should pass', (battery) => {
|
|
14
|
+
battery.test('1 + 1 = 2').value(1 + 1).value(2).equal;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
TestBattery.test('should work with async', async (battery) => {
|
|
18
|
+
battery.test('result = 42').value(Promise.resolve(42)).value(42).equal;
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const config = {
|
|
6
|
+
port: parseInt(process.env.PORT || '3000', 10),
|
|
7
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
8
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate required environment variables
|
|
13
|
+
*/
|
|
14
|
+
export function validateConfig() {
|
|
15
|
+
// Add validation for required env vars here
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import pino from 'pino';
|
|
4
|
+
|
|
5
|
+
const logger = pino({
|
|
6
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Register error handlers
|
|
11
|
+
*/
|
|
12
|
+
export function registerErrorHandlers(app: Application<AppMeta>) {
|
|
13
|
+
app.onError(async (err, req, res) => {
|
|
14
|
+
const level = req.endpointMeta.logging.level;
|
|
15
|
+
|
|
16
|
+
if (level === 'debug' || level === 'error') {
|
|
17
|
+
logger.error({
|
|
18
|
+
error: err.message,
|
|
19
|
+
stack: err.stack,
|
|
20
|
+
method: req.method,
|
|
21
|
+
path: req.path,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!res.headersSent) {
|
|
26
|
+
res.status(500).json({
|
|
27
|
+
error: 'Internal Server Error',
|
|
28
|
+
message: process.env.NODE_ENV === 'development' ? err.message : undefined,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import pino from 'pino';
|
|
4
|
+
|
|
5
|
+
const logger = pino({
|
|
6
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Register finalizers (always run)
|
|
11
|
+
*/
|
|
12
|
+
export function registerFinalizers(app: Application<AppMeta>) {
|
|
13
|
+
app.onFinalize(async (req, res) => {
|
|
14
|
+
const duration = Date.now() - (req._startTime || Date.now());
|
|
15
|
+
const level = req.endpointMeta.logging.level;
|
|
16
|
+
|
|
17
|
+
if (level === 'debug' || level === 'info') {
|
|
18
|
+
logger.info({
|
|
19
|
+
method: req.method,
|
|
20
|
+
path: req.path,
|
|
21
|
+
status: res.statusCode,
|
|
22
|
+
duration,
|
|
23
|
+
event: 'request.complete',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register response transformers
|
|
6
|
+
*/
|
|
7
|
+
export function registerTransformers(app: Application<AppMeta>) {
|
|
8
|
+
// Add custom headers based on tags
|
|
9
|
+
app.onTransform(async (req, res) => {
|
|
10
|
+
const tags = req.endpointMeta.tags;
|
|
11
|
+
|
|
12
|
+
if (tags.includes('api')) {
|
|
13
|
+
res.setHeader('X-API-Version', '1.0');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Add more transformations here
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createApp } from 'filamentjs';
|
|
2
|
+
import { defaultMeta } from './meta/defaults.js';
|
|
3
|
+
import { config, validateConfig } from './config/app.js';
|
|
4
|
+
import { registerMiddleware } from './middleware/index.js';
|
|
5
|
+
import { registerRoutes } from './routes/index.js';
|
|
6
|
+
import { registerErrorHandlers } from './handlers/errors.js';
|
|
7
|
+
import { registerTransformers } from './handlers/transforms.js';
|
|
8
|
+
import { registerFinalizers } from './handlers/finalizers.js';
|
|
9
|
+
|
|
10
|
+
// Validate configuration
|
|
11
|
+
validateConfig();
|
|
12
|
+
|
|
13
|
+
// Create Filament application
|
|
14
|
+
const app = createApp(defaultMeta);
|
|
15
|
+
|
|
16
|
+
// Register middleware
|
|
17
|
+
registerMiddleware(app);
|
|
18
|
+
|
|
19
|
+
// Register routes
|
|
20
|
+
registerRoutes(app);
|
|
21
|
+
|
|
22
|
+
// Register post-request handlers
|
|
23
|
+
registerErrorHandlers(app);
|
|
24
|
+
registerTransformers(app);
|
|
25
|
+
registerFinalizers(app);
|
|
26
|
+
|
|
27
|
+
// Graceful shutdown
|
|
28
|
+
process.on('SIGTERM', async () => {
|
|
29
|
+
console.log('SIGTERM received, shutting down gracefully...');
|
|
30
|
+
await app.close();
|
|
31
|
+
process.exit(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
process.on('SIGINT', async () => {
|
|
35
|
+
console.log('SIGINT received, shutting down gracefully...');
|
|
36
|
+
await app.close();
|
|
37
|
+
process.exit(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Start server
|
|
41
|
+
app.listen(config.port, () => {
|
|
42
|
+
console.log(`🔥 Filament server running on http://localhost:${config.port}`);
|
|
43
|
+
console.log(` Environment: ${config.nodeEnv}`);
|
|
44
|
+
console.log(` Log level: ${config.logLevel}`);
|
|
45
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AppMeta } from './index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default metadata for all endpoints
|
|
5
|
+
*
|
|
6
|
+
* Individual endpoints can override these values with Partial<AppMeta>
|
|
7
|
+
*/
|
|
8
|
+
export const defaultMeta: AppMeta = {
|
|
9
|
+
requiresAuth: false,
|
|
10
|
+
rateLimit: 100,
|
|
11
|
+
logging: {
|
|
12
|
+
level: 'info',
|
|
13
|
+
},
|
|
14
|
+
tags: [],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Common metadata presets for convenience
|
|
19
|
+
*/
|
|
20
|
+
export const PUBLIC: Partial<AppMeta> = {
|
|
21
|
+
requiresAuth: false,
|
|
22
|
+
rateLimit: 100,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const AUTHENTICATED: Partial<AppMeta> = {
|
|
26
|
+
requiresAuth: true,
|
|
27
|
+
rateLimit: 50,
|
|
28
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FrameworkMeta } from 'filamentjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Application metadata interface
|
|
5
|
+
*
|
|
6
|
+
* Extend this interface to add your own metadata properties.
|
|
7
|
+
* Middleware can inspect req.endpointMeta to make decisions.
|
|
8
|
+
*/
|
|
9
|
+
export interface AppMeta extends FrameworkMeta {
|
|
10
|
+
/**
|
|
11
|
+
* Whether this endpoint requires authentication
|
|
12
|
+
*/
|
|
13
|
+
requiresAuth: boolean;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Rate limit for this endpoint (requests per minute)
|
|
17
|
+
*/
|
|
18
|
+
rateLimit: number;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Logging configuration
|
|
22
|
+
*/
|
|
23
|
+
logging: {
|
|
24
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Tags for categorizing endpoints
|
|
29
|
+
*/
|
|
30
|
+
tags: string[];
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import pino from 'pino';
|
|
4
|
+
|
|
5
|
+
const logger = pino({
|
|
6
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
7
|
+
transport: {
|
|
8
|
+
target: 'pino-pretty',
|
|
9
|
+
options: {
|
|
10
|
+
colorize: true,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Register all middleware
|
|
17
|
+
*/
|
|
18
|
+
export function registerMiddleware(app: Application<AppMeta>) {
|
|
19
|
+
// Request logging middleware
|
|
20
|
+
app.use(async (req, res, next) => {
|
|
21
|
+
const level = req.endpointMeta.logging.level;
|
|
22
|
+
|
|
23
|
+
if (level === 'debug' || level === 'info') {
|
|
24
|
+
logger.info({
|
|
25
|
+
method: req.method,
|
|
26
|
+
path: req.path,
|
|
27
|
+
event: 'request.start',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await next();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Add more middleware here
|
|
35
|
+
// Example: Authentication, rate limiting, etc.
|
|
36
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Application } from 'filamentjs';
|
|
2
|
+
import { AppMeta } from '../meta/index.js';
|
|
3
|
+
import { PUBLIC } from '../meta/defaults.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Register all routes
|
|
7
|
+
*/
|
|
8
|
+
export function registerRoutes(app: Application<AppMeta>) {
|
|
9
|
+
// Health check endpoint
|
|
10
|
+
app.get('/health', PUBLIC, async (req, res) => {
|
|
11
|
+
res.json({
|
|
12
|
+
status: 'healthy',
|
|
13
|
+
timestamp: new Date().toISOString(),
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Example endpoint
|
|
18
|
+
app.get('/', PUBLIC, async (req, res) => {
|
|
19
|
+
res.json({
|
|
20
|
+
message: 'Welcome to your Filament API',
|
|
21
|
+
endpoints: {
|
|
22
|
+
health: 'GET /health',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { suite, it, before, after } from 'node:test';
|
|
2
|
+
import { TestBattery } from 'test-battery';
|
|
3
|
+
|
|
4
|
+
suite('Example Test', () => {
|
|
5
|
+
before(() => {
|
|
6
|
+
// Setup code here
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
after(() => {
|
|
10
|
+
// Cleanup code here
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
TestBattery.test('should pass', (battery) => {
|
|
14
|
+
battery.test('1 + 1 = 2').value(1 + 1).value(2).equal;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
TestBattery.test('should work with async', async (battery) => {
|
|
18
|
+
battery.test('result = 42').value(Promise.resolve(42)).value(42).equal;
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const config = {
|
|
6
|
+
port: parseInt(process.env.PORT || '3000', 10),
|
|
7
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
8
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate required environment variables
|
|
13
|
+
*/
|
|
14
|
+
export function validateConfig() {
|
|
15
|
+
// Add validation for required env vars here
|
|
16
|
+
return true;
|
|
17
|
+
}
|