create-frootjs 0.0.0-stage → 1.0.1
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 +68 -2
- package/dist/generator.d.ts +21 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +62 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +215 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/fullstack.d.ts +7 -0
- package/dist/templates/fullstack.d.ts.map +1 -0
- package/dist/templates/fullstack.js +224 -0
- package/dist/templates/fullstack.js.map +1 -0
- package/dist/templates/index.d.ts +10 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +26 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/minimal.d.ts +7 -0
- package/dist/templates/minimal.d.ts.map +1 -0
- package/dist/templates/minimal.js +89 -0
- package/dist/templates/minimal.js.map +1 -0
- package/dist/templates/modular.d.ts +7 -0
- package/dist/templates/modular.d.ts.map +1 -0
- package/dist/templates/modular.js +300 -0
- package/dist/templates/modular.js.map +1 -0
- package/dist/templates/mvc.d.ts +7 -0
- package/dist/templates/mvc.d.ts.map +1 -0
- package/dist/templates/mvc.js +239 -0
- package/dist/templates/mvc.js.map +1 -0
- package/dist/templates/openapi.d.ts +7 -0
- package/dist/templates/openapi.d.ts.map +1 -0
- package/dist/templates/openapi.js +206 -0
- package/dist/templates/openapi.js.map +1 -0
- package/dist/templates/streaming.d.ts +7 -0
- package/dist/templates/streaming.d.ts.map +1 -0
- package/dist/templates/streaming.js +211 -0
- package/dist/templates/streaming.js.map +1 -0
- package/dist/templates/types.d.ts +17 -0
- package/dist/templates/types.d.ts.map +1 -0
- package/dist/templates/types.js +47 -0
- package/dist/templates/types.js.map +1 -0
- package/dist/templates/websocket.d.ts +7 -0
- package/dist/templates/websocket.d.ts.map +1 -0
- package/dist/templates/websocket.js +198 -0
- package/dist/templates/websocket.js.map +1 -0
- package/dist/ui.d.ts +73 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +236 -0
- package/dist/ui.js.map +1 -0
- package/package.json +45 -4
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Registry
|
|
3
|
+
* Central export of all available project architectural templates.
|
|
4
|
+
*/
|
|
5
|
+
import { TemplateDefinition } from './types.js';
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export declare const TEMPLATES: Record<string, TemplateDefinition>;
|
|
8
|
+
export declare const TEMPLATE_LIST: TemplateDefinition[];
|
|
9
|
+
export declare function getTemplate(id: string): TemplateDefinition | undefined;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAShD,cAAc,YAAY,CAAC;AAE3B,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAQxD,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,kBAAkB,EAA6B,CAAC;AAE5E,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAEtE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Registry
|
|
3
|
+
* Central export of all available project architectural templates.
|
|
4
|
+
*/
|
|
5
|
+
import { mvcTemplate } from './mvc.js';
|
|
6
|
+
import { modularTemplate } from './modular.js';
|
|
7
|
+
import { streamingTemplate } from './streaming.js';
|
|
8
|
+
import { websocketTemplate } from './websocket.js';
|
|
9
|
+
import { openapiTemplate } from './openapi.js';
|
|
10
|
+
import { fullstackTemplate } from './fullstack.js';
|
|
11
|
+
import { minimalTemplate } from './minimal.js';
|
|
12
|
+
export * from './types.js';
|
|
13
|
+
export const TEMPLATES = {
|
|
14
|
+
mvc: mvcTemplate,
|
|
15
|
+
modular: modularTemplate,
|
|
16
|
+
streaming: streamingTemplate,
|
|
17
|
+
websocket: websocketTemplate,
|
|
18
|
+
openapi: openapiTemplate,
|
|
19
|
+
fullstack: fullstackTemplate,
|
|
20
|
+
minimal: minimalTemplate,
|
|
21
|
+
};
|
|
22
|
+
export const TEMPLATE_LIST = Object.values(TEMPLATES);
|
|
23
|
+
export function getTemplate(id) {
|
|
24
|
+
return TEMPLATES[id.toLowerCase()];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,cAAc,YAAY,CAAC;AAE3B,MAAM,CAAC,MAAM,SAAS,GAAuC;IAC3D,GAAG,EAAE,WAAW;IAChB,OAAO,EAAE,eAAe;IACxB,SAAS,EAAE,iBAAiB;IAC5B,SAAS,EAAE,iBAAiB;IAC5B,OAAO,EAAE,eAAe;IACxB,SAAS,EAAE,iBAAiB;IAC5B,OAAO,EAAE,eAAe;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAyB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAE5E,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Starter Template
|
|
3
|
+
* Clean, single-file starter project for microservices, quick prototypes, or lightweight APIs.
|
|
4
|
+
*/
|
|
5
|
+
import { TemplateDefinition } from './types.js';
|
|
6
|
+
export declare const minimalTemplate: TemplateDefinition;
|
|
7
|
+
//# sourceMappingURL=minimal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minimal.d.ts","sourceRoot":"","sources":["../../src/templates/minimal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,kBAAkB,EAAsD,MAAM,YAAY,CAAC;AAEpG,eAAO,MAAM,eAAe,EAAE,kBAyF7B,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Starter Template
|
|
3
|
+
* Clean, single-file starter project for microservices, quick prototypes, or lightweight APIs.
|
|
4
|
+
*/
|
|
5
|
+
import { COMMON_GITIGNORE, COMMON_TSCONFIG } from './types.js';
|
|
6
|
+
export const minimalTemplate = {
|
|
7
|
+
id: 'minimal',
|
|
8
|
+
name: 'Minimal Starter (Single-file)',
|
|
9
|
+
description: 'Lean single-file API starter with basic JSON endpoints and CORS',
|
|
10
|
+
generate: (projectName) => {
|
|
11
|
+
return {
|
|
12
|
+
'package.json': JSON.stringify({
|
|
13
|
+
name: projectName,
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
type: 'module',
|
|
16
|
+
scripts: {
|
|
17
|
+
dev: 'node --experimental-strip-types src/index.ts',
|
|
18
|
+
build: 'tsc',
|
|
19
|
+
start: 'node dist/index.js',
|
|
20
|
+
},
|
|
21
|
+
dependencies: {
|
|
22
|
+
frootjs: '^1.0.1',
|
|
23
|
+
},
|
|
24
|
+
devDependencies: {
|
|
25
|
+
'@types/node': '^22.10.0',
|
|
26
|
+
typescript: '^5.8.0',
|
|
27
|
+
},
|
|
28
|
+
}, null, 2) + '\n',
|
|
29
|
+
'tsconfig.json': COMMON_TSCONFIG,
|
|
30
|
+
'.gitignore': COMMON_GITIGNORE,
|
|
31
|
+
'README.md': `# ${projectName}
|
|
32
|
+
|
|
33
|
+
Lean, high-performance web service built with **Froot**.
|
|
34
|
+
|
|
35
|
+
## Getting Started
|
|
36
|
+
|
|
37
|
+
\`\`\`bash
|
|
38
|
+
# 1. Install dependencies
|
|
39
|
+
pnpm install
|
|
40
|
+
|
|
41
|
+
# 2. Run in development mode
|
|
42
|
+
pnpm run dev
|
|
43
|
+
|
|
44
|
+
# 3. Test endpoint
|
|
45
|
+
curl http://localhost:3000/
|
|
46
|
+
\`\`\`
|
|
47
|
+
`,
|
|
48
|
+
'src/index.ts': `import { Froot } from 'frootjs';
|
|
49
|
+
import { cors } from 'frootjs/cors';
|
|
50
|
+
|
|
51
|
+
export const app = new Froot();
|
|
52
|
+
|
|
53
|
+
// Middleware
|
|
54
|
+
app.use(cors());
|
|
55
|
+
|
|
56
|
+
// Routes
|
|
57
|
+
app.get('/', (c) => {
|
|
58
|
+
return c.json({
|
|
59
|
+
message: 'Hello from Froot!',
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
app.get('/health', (c) => {
|
|
65
|
+
return c.json({
|
|
66
|
+
status: 'ok',
|
|
67
|
+
uptime: process.uptime(),
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
app.post('/echo', async (c) => {
|
|
72
|
+
const body = await c.req.json();
|
|
73
|
+
return c.json({
|
|
74
|
+
received: body,
|
|
75
|
+
headers: {
|
|
76
|
+
userAgent: c.req.header('user-agent'),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const PORT = Number(process.env.PORT) || 3000;
|
|
82
|
+
app.listen(PORT, () => {
|
|
83
|
+
console.log(\`Server is running on http://localhost:\${PORT}\`);
|
|
84
|
+
});
|
|
85
|
+
`,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=minimal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minimal.js","sourceRoot":"","sources":["../../src/templates/minimal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAuC,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,CAAC,MAAM,eAAe,GAAuB;IACjD,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,+BAA+B;IACrC,WAAW,EAAE,iEAAiE;IAC9E,QAAQ,EAAE,CAAC,WAAmB,EAAmB,EAAE;QACjD,OAAO;YACL,cAAc,EAAE,IAAI,CAAC,SAAS,CAC5B;gBACE,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE;oBACP,GAAG,EAAE,8CAA8C;oBACnD,KAAK,EAAE,KAAK;oBACZ,KAAK,EAAE,oBAAoB;iBAC5B;gBACD,YAAY,EAAE;oBACZ,OAAO,EAAE,QAAQ;iBAClB;gBACD,eAAe,EAAE;oBACf,aAAa,EAAE,UAAU;oBACzB,UAAU,EAAE,QAAQ;iBACrB;aACF,EACD,IAAI,EACJ,CAAC,CACF,GAAG,IAAI;YAER,eAAe,EAAE,eAAe;YAChC,YAAY,EAAE,gBAAgB;YAE9B,WAAW,EAAE,KAAK,WAAW;;;;;;;;;;;;;;;;CAgBlC;YAEK,cAAc,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCrB;SACI,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modular / NestJS-Style Feature Modules Template
|
|
3
|
+
* Organizes code by domain feature: each module encapsulates its routes, service, schema, and types.
|
|
4
|
+
*/
|
|
5
|
+
import { TemplateDefinition } from './types.js';
|
|
6
|
+
export declare const modularTemplate: TemplateDefinition;
|
|
7
|
+
//# sourceMappingURL=modular.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modular.d.ts","sourceRoot":"","sources":["../../src/templates/modular.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,kBAAkB,EAAsD,MAAM,YAAY,CAAC;AAEpG,eAAO,MAAM,eAAe,EAAE,kBAuT7B,CAAC"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modular / NestJS-Style Feature Modules Template
|
|
3
|
+
* Organizes code by domain feature: each module encapsulates its routes, service, schema, and types.
|
|
4
|
+
*/
|
|
5
|
+
import { COMMON_GITIGNORE, COMMON_TSCONFIG } from './types.js';
|
|
6
|
+
export const modularTemplate = {
|
|
7
|
+
id: 'modular',
|
|
8
|
+
name: 'Modular Architecture (NestJS-style)',
|
|
9
|
+
description: 'Domain feature modules (routes, service, schema) with common guards & filters',
|
|
10
|
+
generate: (projectName) => {
|
|
11
|
+
return {
|
|
12
|
+
'package.json': JSON.stringify({
|
|
13
|
+
name: projectName,
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
type: 'module',
|
|
16
|
+
scripts: {
|
|
17
|
+
dev: 'node --experimental-strip-types src/server.ts',
|
|
18
|
+
build: 'tsc',
|
|
19
|
+
start: 'node dist/server.js',
|
|
20
|
+
test: 'node --test tests/*.test.ts',
|
|
21
|
+
},
|
|
22
|
+
dependencies: {
|
|
23
|
+
frootjs: '^1.0.1',
|
|
24
|
+
},
|
|
25
|
+
devDependencies: {
|
|
26
|
+
'@types/node': '^22.10.0',
|
|
27
|
+
typescript: '^5.8.0',
|
|
28
|
+
},
|
|
29
|
+
}, null, 2) + '\n',
|
|
30
|
+
'tsconfig.json': COMMON_TSCONFIG,
|
|
31
|
+
'.gitignore': COMMON_GITIGNORE,
|
|
32
|
+
'README.md': `# ${projectName}
|
|
33
|
+
|
|
34
|
+
Froot application built with the **Modular Architecture (NestJS-style)** pattern.
|
|
35
|
+
|
|
36
|
+
## Directory Structure
|
|
37
|
+
|
|
38
|
+
\`\`\`
|
|
39
|
+
src/
|
|
40
|
+
├── common/ # Cross-cutting concerns
|
|
41
|
+
│ ├── guards/ # Auth & role guards
|
|
42
|
+
│ │ └── auth.guard.ts
|
|
43
|
+
│ └── filters/ # Global error filters
|
|
44
|
+
│ └── error.filter.ts
|
|
45
|
+
├── modules/ # Feature modules
|
|
46
|
+
│ ├── auth/ # Authentication module (JWT & login)
|
|
47
|
+
│ │ ├── auth.routes.ts
|
|
48
|
+
│ │ └── auth.service.ts
|
|
49
|
+
│ ├── users/ # Users management module
|
|
50
|
+
│ │ ├── users.routes.ts
|
|
51
|
+
│ │ ├── users.service.ts
|
|
52
|
+
│ │ └── users.types.ts
|
|
53
|
+
│ └── products/ # Products catalog module
|
|
54
|
+
│ ├── products.routes.ts
|
|
55
|
+
│ ├── products.service.ts
|
|
56
|
+
│ └── products.types.ts
|
|
57
|
+
├── app.ts # Root application module orchestrator
|
|
58
|
+
└── server.ts # Server bootstrap
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
## Getting Started
|
|
62
|
+
|
|
63
|
+
\`\`\`bash
|
|
64
|
+
# 1. Install dependencies
|
|
65
|
+
pnpm install
|
|
66
|
+
|
|
67
|
+
# 2. Run in development mode
|
|
68
|
+
pnpm run dev
|
|
69
|
+
|
|
70
|
+
# 3. Build for production
|
|
71
|
+
pnpm run build
|
|
72
|
+
pnpm start
|
|
73
|
+
\`\`\`
|
|
74
|
+
`,
|
|
75
|
+
'src/common/guards/auth.guard.ts': `import type { Context, MiddlewareHandler } from 'frootjs';
|
|
76
|
+
import { verifyJwt } from 'frootjs/jwt';
|
|
77
|
+
|
|
78
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
|
|
79
|
+
|
|
80
|
+
export interface AuthContextVariables {
|
|
81
|
+
user: {
|
|
82
|
+
id: string;
|
|
83
|
+
email: string;
|
|
84
|
+
role: string;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const authGuard: MiddlewareHandler = async (c, next) => {
|
|
89
|
+
const authHeader = c.req.header('authorization');
|
|
90
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
91
|
+
return c.json({ error: 'Unauthorized: missing or invalid authorization header' }, 401);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const token = authHeader.slice(7).trim();
|
|
95
|
+
const payload = await verifyJwt(token, JWT_SECRET);
|
|
96
|
+
|
|
97
|
+
if (!payload) {
|
|
98
|
+
return c.json({ error: 'Unauthorized: token is invalid or expired' }, 401);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
c.set('user', payload);
|
|
102
|
+
await next();
|
|
103
|
+
};
|
|
104
|
+
`,
|
|
105
|
+
'src/common/filters/error.filter.ts': `import type { Context } from 'frootjs';
|
|
106
|
+
|
|
107
|
+
export function errorHandler(err: unknown, c: Context): Response {
|
|
108
|
+
console.error('[ErrorFilter] Caught unhandled error:', err);
|
|
109
|
+
|
|
110
|
+
if (err instanceof Error) {
|
|
111
|
+
return c.json(
|
|
112
|
+
{
|
|
113
|
+
error: err.name,
|
|
114
|
+
message: err.message,
|
|
115
|
+
statusCode: 500,
|
|
116
|
+
},
|
|
117
|
+
500
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return c.json(
|
|
122
|
+
{
|
|
123
|
+
error: 'Internal Server Error',
|
|
124
|
+
message: 'An unexpected error occurred',
|
|
125
|
+
statusCode: 500,
|
|
126
|
+
},
|
|
127
|
+
500
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
`,
|
|
131
|
+
'src/modules/auth/auth.service.ts': `import { signJwt } from 'frootjs/jwt';
|
|
132
|
+
|
|
133
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
|
|
134
|
+
|
|
135
|
+
export class AuthService {
|
|
136
|
+
static async createToken(user: { id: string; email: string; role: string }): Promise<string> {
|
|
137
|
+
return signJwt(
|
|
138
|
+
{
|
|
139
|
+
sub: user.id,
|
|
140
|
+
email: user.email,
|
|
141
|
+
role: user.role,
|
|
142
|
+
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24, // 24 hours
|
|
143
|
+
},
|
|
144
|
+
JWT_SECRET
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
`,
|
|
149
|
+
'src/modules/auth/auth.routes.ts': `import { Froot } from 'frootjs';
|
|
150
|
+
import { AuthService } from './auth.service.js';
|
|
151
|
+
|
|
152
|
+
export const authModule = new Froot();
|
|
153
|
+
|
|
154
|
+
authModule.post('/auth/login', async (c) => {
|
|
155
|
+
const body = await c.req.json<{ email?: string; password?: string }>();
|
|
156
|
+
if (!body.email || !body.password) {
|
|
157
|
+
return c.json({ error: 'Email and password required' }, 400);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Demo validation: any credentials pass for showcase
|
|
161
|
+
const user = {
|
|
162
|
+
id: 'usr_demo',
|
|
163
|
+
email: body.email,
|
|
164
|
+
role: 'admin',
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const token = await AuthService.createToken(user);
|
|
168
|
+
return c.json({
|
|
169
|
+
token,
|
|
170
|
+
user,
|
|
171
|
+
message: 'Login successful',
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
`,
|
|
175
|
+
'src/modules/users/users.types.ts': `export interface UserProfile {
|
|
176
|
+
id: string;
|
|
177
|
+
name: string;
|
|
178
|
+
email: string;
|
|
179
|
+
role: string;
|
|
180
|
+
}
|
|
181
|
+
`,
|
|
182
|
+
'src/modules/users/users.service.ts': `import type { UserProfile } from './users.types.js';
|
|
183
|
+
|
|
184
|
+
const mockUsers: UserProfile[] = [
|
|
185
|
+
{ id: 'usr_1', name: 'Ada Lovelace', email: 'ada@froot.dev', role: 'admin' },
|
|
186
|
+
{ id: 'usr_2', name: 'Alan Turing', email: 'alan@froot.dev', role: 'engineer' },
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
export class UsersService {
|
|
190
|
+
static getAll(): UserProfile[] {
|
|
191
|
+
return mockUsers;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static getById(id: string): UserProfile | undefined {
|
|
195
|
+
return mockUsers.find((u) => u.id === id);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
`,
|
|
199
|
+
'src/modules/users/users.routes.ts': `import { Froot } from 'frootjs';
|
|
200
|
+
import { UsersService } from './users.service.js';
|
|
201
|
+
import { authGuard } from '../../common/guards/auth.guard.js';
|
|
202
|
+
|
|
203
|
+
export const usersModule = new Froot();
|
|
204
|
+
|
|
205
|
+
// Public route: list users
|
|
206
|
+
usersModule.get('/users', (c) => {
|
|
207
|
+
return c.json(UsersService.getAll());
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Protected route: user details (guarded by JWT)
|
|
211
|
+
usersModule.get('/users/:id', authGuard, (c) => {
|
|
212
|
+
const id = c.req.param('id');
|
|
213
|
+
const user = UsersService.getById(id);
|
|
214
|
+
if (!user) {
|
|
215
|
+
return c.json({ error: 'User not found' }, 404);
|
|
216
|
+
}
|
|
217
|
+
return c.json(user);
|
|
218
|
+
});
|
|
219
|
+
`,
|
|
220
|
+
'src/modules/products/products.types.ts': `export interface Product {
|
|
221
|
+
id: string;
|
|
222
|
+
title: string;
|
|
223
|
+
price: number;
|
|
224
|
+
inventory: number;
|
|
225
|
+
}
|
|
226
|
+
`,
|
|
227
|
+
'src/modules/products/products.service.ts': `import type { Product } from './products.types.js';
|
|
228
|
+
|
|
229
|
+
const productsTable: Product[] = [
|
|
230
|
+
{ id: 'prod_1', title: 'High-Performance Microservice', price: 99.0, inventory: 50 },
|
|
231
|
+
{ id: 'prod_2', title: 'Real-Time Edge Gateway', price: 149.0, inventory: 25 },
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
export class ProductsService {
|
|
235
|
+
static list(): Product[] {
|
|
236
|
+
return productsTable;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
static find(id: string): Product | undefined {
|
|
240
|
+
return productsTable.find((p) => p.id === id);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
`,
|
|
244
|
+
'src/modules/products/products.routes.ts': `import { Froot } from 'frootjs';
|
|
245
|
+
import { ProductsService } from './products.service.js';
|
|
246
|
+
|
|
247
|
+
export const productsModule = new Froot();
|
|
248
|
+
|
|
249
|
+
productsModule.get('/products', (c) => {
|
|
250
|
+
return c.json(ProductsService.list());
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
productsModule.get('/products/:id', (c) => {
|
|
254
|
+
const id = c.req.param('id');
|
|
255
|
+
const product = ProductsService.find(id);
|
|
256
|
+
if (!product) {
|
|
257
|
+
return c.json({ error: 'Product not found' }, 404);
|
|
258
|
+
}
|
|
259
|
+
return c.json(product);
|
|
260
|
+
});
|
|
261
|
+
`,
|
|
262
|
+
'src/app.ts': `import { Froot } from 'frootjs';
|
|
263
|
+
import { cors } from 'frootjs/cors';
|
|
264
|
+
import { errorHandler } from './common/filters/error.filter.js';
|
|
265
|
+
import { authModule } from './modules/auth/auth.routes.js';
|
|
266
|
+
import { usersModule } from './modules/users/users.routes.js';
|
|
267
|
+
import { productsModule } from './modules/products/products.routes.js';
|
|
268
|
+
|
|
269
|
+
export const app = new Froot();
|
|
270
|
+
|
|
271
|
+
// Global middleware & filters
|
|
272
|
+
app.use(cors());
|
|
273
|
+
app.onError(errorHandler);
|
|
274
|
+
|
|
275
|
+
// Mount feature modules under /api prefix
|
|
276
|
+
app.route('/api', authModule);
|
|
277
|
+
app.route('/api', usersModule);
|
|
278
|
+
app.route('/api', productsModule);
|
|
279
|
+
|
|
280
|
+
// Health check
|
|
281
|
+
app.get('/health', (c) => {
|
|
282
|
+
return c.json({
|
|
283
|
+
status: 'ok',
|
|
284
|
+
uptime: process.uptime(),
|
|
285
|
+
modules: ['auth', 'users', 'products'],
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
`,
|
|
289
|
+
'src/server.ts': `import { app } from './app.js';
|
|
290
|
+
|
|
291
|
+
const PORT = Number(process.env.PORT) || 3000;
|
|
292
|
+
|
|
293
|
+
app.listen(PORT, () => {
|
|
294
|
+
console.log(\`Modular Froot Server running on http://localhost:\${PORT}\`);
|
|
295
|
+
});
|
|
296
|
+
`,
|
|
297
|
+
};
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
//# sourceMappingURL=modular.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modular.js","sourceRoot":"","sources":["../../src/templates/modular.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAuC,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,CAAC,MAAM,eAAe,GAAuB;IACjD,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,qCAAqC;IAC3C,WAAW,EAAE,+EAA+E;IAC5F,QAAQ,EAAE,CAAC,WAAmB,EAAmB,EAAE;QACjD,OAAO;YACL,cAAc,EAAE,IAAI,CAAC,SAAS,CAC5B;gBACE,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE;oBACP,GAAG,EAAE,+CAA+C;oBACpD,KAAK,EAAE,KAAK;oBACZ,KAAK,EAAE,qBAAqB;oBAC5B,IAAI,EAAE,6BAA6B;iBACpC;gBACD,YAAY,EAAE;oBACZ,OAAO,EAAE,QAAQ;iBAClB;gBACD,eAAe,EAAE;oBACf,aAAa,EAAE,UAAU;oBACzB,UAAU,EAAE,QAAQ;iBACrB;aACF,EACD,IAAI,EACJ,CAAC,CACF,GAAG,IAAI;YAER,eAAe,EAAE,eAAe;YAChC,YAAY,EAAE,gBAAgB;YAE9B,WAAW,EAAE,KAAK,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0ClC;YAEK,iCAAiC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BxC;YAEK,oCAAoC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;CAyB3C;YAEK,kCAAkC,EAAE;;;;;;;;;;;;;;;;;CAiBzC;YAEK,iCAAiC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;CAyBxC;YAEK,kCAAkC,EAAE;;;;;;CAMzC;YAEK,oCAAoC,EAAE;;;;;;;;;;;;;;;;CAgB3C;YAEK,mCAAmC,EAAE;;;;;;;;;;;;;;;;;;;;CAoB1C;YAEK,wCAAwC,EAAE;;;;;;CAM/C;YAEK,0CAA0C,EAAE;;;;;;;;;;;;;;;;CAgBjD;YAEK,yCAAyC,EAAE;;;;;;;;;;;;;;;;;CAiBhD;YAEK,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BnB;YAEK,eAAe,EAAE;;;;;;;CAOtB;SACI,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mvc.d.ts","sourceRoot":"","sources":["../../src/templates/mvc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,kBAAkB,EAAsD,MAAM,YAAY,CAAC;AAEpG,eAAO,MAAM,WAAW,EAAE,kBAwPzB,CAAC"}
|