create-frootjs 1.0.8 → 1.0.10
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/dist/addons/testing.js +2 -2
- package/dist/addons/testing.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/templates/fullstack.d.ts +3 -2
- package/dist/templates/fullstack.d.ts.map +1 -1
- package/dist/templates/fullstack.js +282 -134
- package/dist/templates/fullstack.js.map +1 -1
- package/dist/templates/minimal.js +54 -54
- package/dist/templates/modular.js +261 -261
- package/dist/templates/mvc.js +196 -196
- package/dist/templates/openapi.js +168 -168
- package/dist/templates/streaming.js +176 -176
- package/dist/templates/websocket.js +163 -163
- package/package.json +1 -1
package/dist/templates/mvc.js
CHANGED
|
@@ -20,7 +20,7 @@ export const mvcTemplate = {
|
|
|
20
20
|
test: 'node --test tests/*.test.ts',
|
|
21
21
|
},
|
|
22
22
|
dependencies: {
|
|
23
|
-
frootjs: '^1.0.
|
|
23
|
+
frootjs: '^1.0.5',
|
|
24
24
|
},
|
|
25
25
|
devDependencies: {
|
|
26
26
|
'@types/node': '^22.10.0',
|
|
@@ -30,211 +30,211 @@ export const mvcTemplate = {
|
|
|
30
30
|
}, null, 2) + '\n',
|
|
31
31
|
'tsconfig.json': COMMON_TSCONFIG,
|
|
32
32
|
'.gitignore': COMMON_GITIGNORE,
|
|
33
|
-
'README.md': `# ${projectName}
|
|
34
|
-
|
|
35
|
-
Froot application built with the **MVC / Layered Architecture** pattern.
|
|
36
|
-
|
|
37
|
-
## Directory Structure
|
|
38
|
-
|
|
39
|
-
\`\`\`
|
|
40
|
-
src/
|
|
41
|
-
├── controllers/ # Request handlers & HTTP response formatting
|
|
42
|
-
│ ├── health.controller.ts
|
|
43
|
-
│ └── user.controller.ts
|
|
44
|
-
├── services/ # Core business logic & database interaction
|
|
45
|
-
│ └── user.service.ts
|
|
46
|
-
├── models/ # TypeScript interfaces & domain types
|
|
47
|
-
│ └── user.model.ts
|
|
48
|
-
├── routes/ # Froot route definitions & sub-app mounting
|
|
49
|
-
│ ├── health.routes.ts
|
|
50
|
-
│ ├── user.routes.ts
|
|
51
|
-
│ └── index.ts
|
|
52
|
-
├── middleware/ # Custom onion middleware
|
|
53
|
-
│ └── request-logger.ts
|
|
54
|
-
├── app.ts # Application assembly
|
|
55
|
-
└── server.ts # Server entry point
|
|
56
|
-
\`\`\`
|
|
57
|
-
|
|
58
|
-
## Getting Started
|
|
59
|
-
|
|
60
|
-
\`\`\`bash
|
|
61
|
-
# 1. Install dependencies
|
|
62
|
-
pnpm install
|
|
63
|
-
|
|
64
|
-
# 2. Run in development mode
|
|
65
|
-
pnpm run dev
|
|
66
|
-
|
|
67
|
-
# 3. Build for production
|
|
68
|
-
pnpm run build
|
|
69
|
-
pnpm start
|
|
70
|
-
\`\`\`
|
|
33
|
+
'README.md': `# ${projectName}
|
|
34
|
+
|
|
35
|
+
Froot application built with the **MVC / Layered Architecture** pattern.
|
|
36
|
+
|
|
37
|
+
## Directory Structure
|
|
38
|
+
|
|
39
|
+
\`\`\`
|
|
40
|
+
src/
|
|
41
|
+
├── controllers/ # Request handlers & HTTP response formatting
|
|
42
|
+
│ ├── health.controller.ts
|
|
43
|
+
│ └── user.controller.ts
|
|
44
|
+
├── services/ # Core business logic & database interaction
|
|
45
|
+
│ └── user.service.ts
|
|
46
|
+
├── models/ # TypeScript interfaces & domain types
|
|
47
|
+
│ └── user.model.ts
|
|
48
|
+
├── routes/ # Froot route definitions & sub-app mounting
|
|
49
|
+
│ ├── health.routes.ts
|
|
50
|
+
│ ├── user.routes.ts
|
|
51
|
+
│ └── index.ts
|
|
52
|
+
├── middleware/ # Custom onion middleware
|
|
53
|
+
│ └── request-logger.ts
|
|
54
|
+
├── app.ts # Application assembly
|
|
55
|
+
└── server.ts # Server entry point
|
|
56
|
+
\`\`\`
|
|
57
|
+
|
|
58
|
+
## Getting Started
|
|
59
|
+
|
|
60
|
+
\`\`\`bash
|
|
61
|
+
# 1. Install dependencies
|
|
62
|
+
pnpm install
|
|
63
|
+
|
|
64
|
+
# 2. Run in development mode
|
|
65
|
+
pnpm run dev
|
|
66
|
+
|
|
67
|
+
# 3. Build for production
|
|
68
|
+
pnpm run build
|
|
69
|
+
pnpm start
|
|
70
|
+
\`\`\`
|
|
71
71
|
`,
|
|
72
|
-
'src/models/user.model.ts': `export interface User {
|
|
73
|
-
id: string;
|
|
74
|
-
name: string;
|
|
75
|
-
email: string;
|
|
76
|
-
role: 'admin' | 'user' | 'guest';
|
|
77
|
-
createdAt: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface CreateUserInput {
|
|
81
|
-
name: string;
|
|
82
|
-
email: string;
|
|
83
|
-
role?: 'admin' | 'user' | 'guest';
|
|
84
|
-
}
|
|
72
|
+
'src/models/user.model.ts': `export interface User {
|
|
73
|
+
id: string;
|
|
74
|
+
name: string;
|
|
75
|
+
email: string;
|
|
76
|
+
role: 'admin' | 'user' | 'guest';
|
|
77
|
+
createdAt: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface CreateUserInput {
|
|
81
|
+
name: string;
|
|
82
|
+
email: string;
|
|
83
|
+
role?: 'admin' | 'user' | 'guest';
|
|
84
|
+
}
|
|
85
85
|
`,
|
|
86
|
-
'src/services/user.service.ts': `import type { User, CreateUserInput } from '../models/user.model.js';
|
|
87
|
-
|
|
88
|
-
// In-memory mock database
|
|
89
|
-
const usersTable = new Map<string, User>([
|
|
90
|
-
['usr_1', { id: 'usr_1', name: 'Alice Smith', email: 'alice@example.com', role: 'admin', createdAt: new Date().toISOString() }],
|
|
91
|
-
['usr_2', { id: 'usr_2', name: 'Bob Jones', email: 'bob@example.com', role: 'user', createdAt: new Date().toISOString() }],
|
|
92
|
-
]);
|
|
93
|
-
|
|
94
|
-
export class UserService {
|
|
95
|
-
static async findAll(): Promise<User[]> {
|
|
96
|
-
return Array.from(usersTable.values());
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
static async findById(id?: string): Promise<User | null> {
|
|
100
|
-
if (!id) return null;
|
|
101
|
-
return usersTable.get(id) ?? null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
static async create(input: CreateUserInput): Promise<User> {
|
|
105
|
-
const id = 'usr_' + Math.random().toString(36).substring(2, 9);
|
|
106
|
-
const user: User = {
|
|
107
|
-
id,
|
|
108
|
-
name: input.name,
|
|
109
|
-
email: input.email,
|
|
110
|
-
role: input.role ?? 'user',
|
|
111
|
-
createdAt: new Date().toISOString(),
|
|
112
|
-
};
|
|
113
|
-
usersTable.set(id, user);
|
|
114
|
-
return user;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
static async delete(id?: string): Promise<boolean> {
|
|
118
|
-
if (!id) return false;
|
|
119
|
-
return usersTable.delete(id);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
86
|
+
'src/services/user.service.ts': `import type { User, CreateUserInput } from '../models/user.model.js';
|
|
87
|
+
|
|
88
|
+
// In-memory mock database
|
|
89
|
+
const usersTable = new Map<string, User>([
|
|
90
|
+
['usr_1', { id: 'usr_1', name: 'Alice Smith', email: 'alice@example.com', role: 'admin', createdAt: new Date().toISOString() }],
|
|
91
|
+
['usr_2', { id: 'usr_2', name: 'Bob Jones', email: 'bob@example.com', role: 'user', createdAt: new Date().toISOString() }],
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
export class UserService {
|
|
95
|
+
static async findAll(): Promise<User[]> {
|
|
96
|
+
return Array.from(usersTable.values());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static async findById(id?: string): Promise<User | null> {
|
|
100
|
+
if (!id) return null;
|
|
101
|
+
return usersTable.get(id) ?? null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static async create(input: CreateUserInput): Promise<User> {
|
|
105
|
+
const id = 'usr_' + Math.random().toString(36).substring(2, 9);
|
|
106
|
+
const user: User = {
|
|
107
|
+
id,
|
|
108
|
+
name: input.name,
|
|
109
|
+
email: input.email,
|
|
110
|
+
role: input.role ?? 'user',
|
|
111
|
+
createdAt: new Date().toISOString(),
|
|
112
|
+
};
|
|
113
|
+
usersTable.set(id, user);
|
|
114
|
+
return user;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static async delete(id?: string): Promise<boolean> {
|
|
118
|
+
if (!id) return false;
|
|
119
|
+
return usersTable.delete(id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
122
|
`,
|
|
123
|
-
'src/controllers/health.controller.ts': `import type { Context } from 'frootjs';
|
|
124
|
-
|
|
125
|
-
export class HealthController {
|
|
126
|
-
static getHealth(c: Context) {
|
|
127
|
-
return c.json({
|
|
128
|
-
status: 'ok',
|
|
129
|
-
uptime: process.uptime(),
|
|
130
|
-
timestamp: new Date().toISOString(),
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
123
|
+
'src/controllers/health.controller.ts': `import type { Context } from 'frootjs';
|
|
124
|
+
|
|
125
|
+
export class HealthController {
|
|
126
|
+
static getHealth(c: Context) {
|
|
127
|
+
return c.json({
|
|
128
|
+
status: 'ok',
|
|
129
|
+
uptime: process.uptime(),
|
|
130
|
+
timestamp: new Date().toISOString(),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
134
|
`,
|
|
135
|
-
'src/controllers/user.controller.ts': `import type { Context } from 'frootjs';
|
|
136
|
-
import { UserService } from '../services/user.service.js';
|
|
137
|
-
import type { CreateUserInput } from '../models/user.model.js';
|
|
138
|
-
|
|
139
|
-
export class UserController {
|
|
140
|
-
static async list(c: Context) {
|
|
141
|
-
const users = await UserService.findAll();
|
|
142
|
-
return c.json(users);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
static async getById(c: Context) {
|
|
146
|
-
const id = c.req.param('id');
|
|
147
|
-
const user = await UserService.findById(id);
|
|
148
|
-
if (!user) {
|
|
149
|
-
return c.json({ error: 'User not found' }, 404);
|
|
150
|
-
}
|
|
151
|
-
return c.json(user);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
static async create(c: Context) {
|
|
155
|
-
const body = await c.req.json<CreateUserInput>();
|
|
156
|
-
if (!body.name || !body.email) {
|
|
157
|
-
return c.json({ error: 'Name and email are required' }, 400);
|
|
158
|
-
}
|
|
159
|
-
const user = await UserService.create(body);
|
|
160
|
-
return c.json(user, 201);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
static async remove(c: Context) {
|
|
164
|
-
const id = c.req.param('id');
|
|
165
|
-
const ok = await UserService.delete(id);
|
|
166
|
-
if (!ok) {
|
|
167
|
-
return c.json({ error: 'User not found' }, 404);
|
|
168
|
-
}
|
|
169
|
-
return c.json({ success: true, message: 'User deleted' });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
135
|
+
'src/controllers/user.controller.ts': `import type { Context } from 'frootjs';
|
|
136
|
+
import { UserService } from '../services/user.service.js';
|
|
137
|
+
import type { CreateUserInput } from '../models/user.model.js';
|
|
138
|
+
|
|
139
|
+
export class UserController {
|
|
140
|
+
static async list(c: Context) {
|
|
141
|
+
const users = await UserService.findAll();
|
|
142
|
+
return c.json(users);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static async getById(c: Context) {
|
|
146
|
+
const id = c.req.param('id');
|
|
147
|
+
const user = await UserService.findById(id);
|
|
148
|
+
if (!user) {
|
|
149
|
+
return c.json({ error: 'User not found' }, 404);
|
|
150
|
+
}
|
|
151
|
+
return c.json(user);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static async create(c: Context) {
|
|
155
|
+
const body = await c.req.json<CreateUserInput>();
|
|
156
|
+
if (!body.name || !body.email) {
|
|
157
|
+
return c.json({ error: 'Name and email are required' }, 400);
|
|
158
|
+
}
|
|
159
|
+
const user = await UserService.create(body);
|
|
160
|
+
return c.json(user, 201);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static async remove(c: Context) {
|
|
164
|
+
const id = c.req.param('id');
|
|
165
|
+
const ok = await UserService.delete(id);
|
|
166
|
+
if (!ok) {
|
|
167
|
+
return c.json({ error: 'User not found' }, 404);
|
|
168
|
+
}
|
|
169
|
+
return c.json({ success: true, message: 'User deleted' });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
172
|
`,
|
|
173
|
-
'src/routes/health.routes.ts': `import { Froot } from 'frootjs';
|
|
174
|
-
import { HealthController } from '../controllers/health.controller.js';
|
|
175
|
-
|
|
176
|
-
export const healthRoutes = new Froot();
|
|
177
|
-
|
|
178
|
-
healthRoutes.get('/health', HealthController.getHealth);
|
|
173
|
+
'src/routes/health.routes.ts': `import { Froot } from 'frootjs';
|
|
174
|
+
import { HealthController } from '../controllers/health.controller.js';
|
|
175
|
+
|
|
176
|
+
export const healthRoutes = new Froot();
|
|
177
|
+
|
|
178
|
+
healthRoutes.get('/health', HealthController.getHealth);
|
|
179
179
|
`,
|
|
180
|
-
'src/routes/user.routes.ts': `import { Froot } from 'frootjs';
|
|
181
|
-
import { UserController } from '../controllers/user.controller.js';
|
|
182
|
-
|
|
183
|
-
export const userRoutes = new Froot();
|
|
184
|
-
|
|
185
|
-
userRoutes.get('/users', UserController.list);
|
|
186
|
-
userRoutes.get('/users/:id', UserController.getById);
|
|
187
|
-
userRoutes.post('/users', UserController.create);
|
|
188
|
-
userRoutes.delete('/users/:id', UserController.remove);
|
|
180
|
+
'src/routes/user.routes.ts': `import { Froot } from 'frootjs';
|
|
181
|
+
import { UserController } from '../controllers/user.controller.js';
|
|
182
|
+
|
|
183
|
+
export const userRoutes = new Froot();
|
|
184
|
+
|
|
185
|
+
userRoutes.get('/users', UserController.list);
|
|
186
|
+
userRoutes.get('/users/:id', UserController.getById);
|
|
187
|
+
userRoutes.post('/users', UserController.create);
|
|
188
|
+
userRoutes.delete('/users/:id', UserController.remove);
|
|
189
189
|
`,
|
|
190
|
-
'src/routes/index.ts': `import { Froot } from 'frootjs';
|
|
191
|
-
import { healthRoutes } from './health.routes.js';
|
|
192
|
-
import { userRoutes } from './user.routes.js';
|
|
193
|
-
|
|
194
|
-
export const apiRoutes = new Froot();
|
|
195
|
-
|
|
196
|
-
// Mount sub-apps
|
|
197
|
-
apiRoutes.route('/api', healthRoutes);
|
|
198
|
-
apiRoutes.route('/api', userRoutes);
|
|
190
|
+
'src/routes/index.ts': `import { Froot } from 'frootjs';
|
|
191
|
+
import { healthRoutes } from './health.routes.js';
|
|
192
|
+
import { userRoutes } from './user.routes.js';
|
|
193
|
+
|
|
194
|
+
export const apiRoutes = new Froot();
|
|
195
|
+
|
|
196
|
+
// Mount sub-apps
|
|
197
|
+
apiRoutes.route('/api', healthRoutes);
|
|
198
|
+
apiRoutes.route('/api', userRoutes);
|
|
199
199
|
`,
|
|
200
|
-
'src/middleware/request-logger.ts': `import type { MiddlewareHandler } from 'frootjs';
|
|
201
|
-
|
|
202
|
-
export const requestLogger: MiddlewareHandler = async (c, next) => {
|
|
203
|
-
const start = performance.now();
|
|
204
|
-
await next();
|
|
205
|
-
const duration = (performance.now() - start).toFixed(2);
|
|
206
|
-
console.log(\`[\${c.req.method}] \${c.req.path} - \${c.statusCode} (\${duration}ms)\`);
|
|
207
|
-
};
|
|
200
|
+
'src/middleware/request-logger.ts': `import type { MiddlewareHandler } from 'frootjs';
|
|
201
|
+
|
|
202
|
+
export const requestLogger: MiddlewareHandler = async (c, next) => {
|
|
203
|
+
const start = performance.now();
|
|
204
|
+
await next();
|
|
205
|
+
const duration = (performance.now() - start).toFixed(2);
|
|
206
|
+
console.log(\`[\${c.req.method}] \${c.req.path} - \${c.statusCode} (\${duration}ms)\`);
|
|
207
|
+
};
|
|
208
208
|
`,
|
|
209
|
-
'src/app.ts': `import { Froot } from 'frootjs';
|
|
210
|
-
import { cors } from 'frootjs/cors';
|
|
211
|
-
import { requestLogger } from './middleware/request-logger.js';
|
|
212
|
-
import { apiRoutes } from './routes/index.js';
|
|
213
|
-
|
|
214
|
-
export const app = new Froot();
|
|
215
|
-
|
|
216
|
-
// Global middleware
|
|
217
|
-
app.use(cors());
|
|
218
|
-
app.use(requestLogger);
|
|
219
|
-
|
|
220
|
-
// Mount API routes
|
|
221
|
-
app.route('', apiRoutes);
|
|
222
|
-
|
|
223
|
-
// Root route
|
|
224
|
-
app.get('/', (c) => {
|
|
225
|
-
return c.json({
|
|
226
|
-
message: 'Welcome to Froot MVC API',
|
|
227
|
-
docs: '/api/health',
|
|
228
|
-
});
|
|
229
|
-
});
|
|
209
|
+
'src/app.ts': `import { Froot } from 'frootjs';
|
|
210
|
+
import { cors } from 'frootjs/cors';
|
|
211
|
+
import { requestLogger } from './middleware/request-logger.js';
|
|
212
|
+
import { apiRoutes } from './routes/index.js';
|
|
213
|
+
|
|
214
|
+
export const app = new Froot();
|
|
215
|
+
|
|
216
|
+
// Global middleware
|
|
217
|
+
app.use(cors());
|
|
218
|
+
app.use(requestLogger);
|
|
219
|
+
|
|
220
|
+
// Mount API routes
|
|
221
|
+
app.route('', apiRoutes);
|
|
222
|
+
|
|
223
|
+
// Root route
|
|
224
|
+
app.get('/', (c) => {
|
|
225
|
+
return c.json({
|
|
226
|
+
message: 'Welcome to Froot MVC API',
|
|
227
|
+
docs: '/api/health',
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
230
|
`,
|
|
231
|
-
'src/server.ts': `import { app } from './app.js';
|
|
232
|
-
|
|
233
|
-
const PORT = Number(process.env.PORT) || 3000;
|
|
234
|
-
|
|
235
|
-
app.listen(PORT, () => {
|
|
236
|
-
console.log(\`Server is running on http://localhost:\${PORT}\`);
|
|
237
|
-
});
|
|
231
|
+
'src/server.ts': `import { app } from './app.js';
|
|
232
|
+
|
|
233
|
+
const PORT = Number(process.env.PORT) || 3000;
|
|
234
|
+
|
|
235
|
+
app.listen(PORT, () => {
|
|
236
|
+
console.log(\`Server is running on http://localhost:\${PORT}\`);
|
|
237
|
+
});
|
|
238
238
|
`,
|
|
239
239
|
};
|
|
240
240
|
},
|