create-frootjs 1.0.13 → 1.0.15

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.
@@ -19,7 +19,7 @@ export const minimalTemplate = {
19
19
  start: 'node dist/index.js',
20
20
  },
21
21
  dependencies: {
22
- frootjs: '^1.0.6',
22
+ frootjs: '^1.0.2',
23
23
  },
24
24
  devDependencies: {
25
25
  '@types/node': '^22.10.0',
@@ -29,60 +29,60 @@ export const minimalTemplate = {
29
29
  }, null, 2) + '\n',
30
30
  'tsconfig.json': COMMON_TSCONFIG,
31
31
  '.gitignore': COMMON_GITIGNORE,
32
- 'README.md': `# ${projectName}
33
-
34
- Lean, high-performance web service built with **Froot**.
35
-
36
- ## Getting Started
37
-
38
- \`\`\`bash
39
- # 1. Install dependencies
40
- pnpm install
41
-
42
- # 2. Run in development mode
43
- pnpm run dev
44
-
45
- # 3. Test endpoint
46
- curl http://localhost:3000/
47
- \`\`\`
32
+ 'README.md': `# ${projectName}
33
+
34
+ Lean, high-performance web service built with **Froot**.
35
+
36
+ ## Getting Started
37
+
38
+ \`\`\`bash
39
+ # 1. Install dependencies
40
+ pnpm install
41
+
42
+ # 2. Run in development mode
43
+ pnpm run dev
44
+
45
+ # 3. Test endpoint
46
+ curl http://localhost:3000/
47
+ \`\`\`
48
48
  `,
49
- 'src/index.ts': `import { Froot } from 'frootjs';
50
- import { cors } from 'frootjs/cors';
51
-
52
- export const app = new Froot();
53
-
54
- // Middleware
55
- app.use(cors());
56
-
57
- // Routes
58
- app.get('/', (c) => {
59
- return c.json({
60
- message: 'Hello from Froot!',
61
- timestamp: new Date().toISOString(),
62
- });
63
- });
64
-
65
- app.get('/health', (c) => {
66
- return c.json({
67
- status: 'ok',
68
- uptime: process.uptime(),
69
- });
70
- });
71
-
72
- app.post('/echo', async (c) => {
73
- const body = await c.req.json();
74
- return c.json({
75
- received: body,
76
- headers: {
77
- userAgent: c.req.header('user-agent'),
78
- },
79
- });
80
- });
81
-
82
- const PORT = Number(process.env.PORT) || 3000;
83
- app.listen(PORT, () => {
84
- console.log(\`Server is running on http://localhost:\${PORT}\`);
85
- });
49
+ 'src/index.ts': `import { Froot } from 'frootjs';
50
+ import { cors } from 'frootjs/cors';
51
+
52
+ export const app = new Froot();
53
+
54
+ // Middleware
55
+ app.use(cors());
56
+
57
+ // Routes
58
+ app.get('/', (c) => {
59
+ return c.json({
60
+ message: 'Hello from Froot!',
61
+ timestamp: new Date().toISOString(),
62
+ });
63
+ });
64
+
65
+ app.get('/health', (c) => {
66
+ return c.json({
67
+ status: 'ok',
68
+ uptime: process.uptime(),
69
+ });
70
+ });
71
+
72
+ app.post('/echo', async (c) => {
73
+ const body = await c.req.json();
74
+ return c.json({
75
+ received: body,
76
+ headers: {
77
+ userAgent: c.req.header('user-agent'),
78
+ },
79
+ });
80
+ });
81
+
82
+ const PORT = Number(process.env.PORT) || 3000;
83
+ app.listen(PORT, () => {
84
+ console.log(\`Server is running on http://localhost:\${PORT}\`);
85
+ });
86
86
  `,
87
87
  };
88
88
  },
@@ -20,7 +20,7 @@ export const modularTemplate = {
20
20
  test: 'node --test tests/*.test.ts',
21
21
  },
22
22
  dependencies: {
23
- frootjs: '^1.0.6',
23
+ frootjs: '^1.0.2',
24
24
  },
25
25
  devDependencies: {
26
26
  '@types/node': '^22.10.0',
@@ -30,278 +30,278 @@ export const modularTemplate = {
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 **Modular Architecture (NestJS-style)** pattern.
36
-
37
- ## Directory Structure
38
-
39
- \`\`\`
40
- src/
41
- ├── common/ # Cross-cutting concerns
42
- │ ├── guards/ # Auth & role guards
43
- │ │ └── auth.guard.ts
44
- │ └── filters/ # Global error filters
45
- │ └── error.filter.ts
46
- ├── modules/ # Feature modules
47
- │ ├── auth/ # Authentication module (JWT & login)
48
- │ │ ├── auth.routes.ts
49
- │ │ └── auth.service.ts
50
- │ ├── users/ # Users management module
51
- │ │ ├── users.routes.ts
52
- │ │ ├── users.service.ts
53
- │ │ └── users.types.ts
54
- │ └── products/ # Products catalog module
55
- │ ├── products.routes.ts
56
- │ ├── products.service.ts
57
- │ └── products.types.ts
58
- ├── app.ts # Root application module orchestrator
59
- └── server.ts # Server bootstrap
60
- \`\`\`
61
-
62
- ## Getting Started
63
-
64
- \`\`\`bash
65
- # 1. Install dependencies
66
- pnpm install
67
-
68
- # 2. Run in development mode
69
- pnpm run dev
70
-
71
- # 3. Build for production
72
- pnpm run build
73
- pnpm start
74
- \`\`\`
33
+ 'README.md': `# ${projectName}
34
+
35
+ Froot application built with the **Modular Architecture (NestJS-style)** pattern.
36
+
37
+ ## Directory Structure
38
+
39
+ \`\`\`
40
+ src/
41
+ ├── common/ # Cross-cutting concerns
42
+ │ ├── guards/ # Auth & role guards
43
+ │ │ └── auth.guard.ts
44
+ │ └── filters/ # Global error filters
45
+ │ └── error.filter.ts
46
+ ├── modules/ # Feature modules
47
+ │ ├── auth/ # Authentication module (JWT & login)
48
+ │ │ ├── auth.routes.ts
49
+ │ │ └── auth.service.ts
50
+ │ ├── users/ # Users management module
51
+ │ │ ├── users.routes.ts
52
+ │ │ ├── users.service.ts
53
+ │ │ └── users.types.ts
54
+ │ └── products/ # Products catalog module
55
+ │ ├── products.routes.ts
56
+ │ ├── products.service.ts
57
+ │ └── products.types.ts
58
+ ├── app.ts # Root application module orchestrator
59
+ └── server.ts # Server bootstrap
60
+ \`\`\`
61
+
62
+ ## Getting Started
63
+
64
+ \`\`\`bash
65
+ # 1. Install dependencies
66
+ pnpm install
67
+
68
+ # 2. Run in development mode
69
+ pnpm run dev
70
+
71
+ # 3. Build for production
72
+ pnpm run build
73
+ pnpm start
74
+ \`\`\`
75
75
  `,
76
- 'src/common/guards/auth.guard.ts': `import type { Context, MiddlewareHandler } from 'frootjs';
77
- import { verifyJwt } from 'frootjs/jwt';
78
-
79
- const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
80
-
81
- export interface AuthContextVariables {
82
- user: {
83
- id: string;
84
- email: string;
85
- role: string;
86
- };
87
- }
88
-
89
- export const authGuard: MiddlewareHandler = async (c, next) => {
90
- const authHeader = c.req.header('authorization');
91
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
92
- return c.json({ error: 'Unauthorized: missing or invalid authorization header' }, 401);
93
- }
94
-
95
- const token = authHeader.slice(7).trim();
96
- try {
97
- const payload = await verifyJwt(token, JWT_SECRET);
98
- c.set('user', payload);
99
- await next();
100
- } catch {
101
- return c.json({ error: 'Unauthorized: token is invalid or expired' }, 401);
102
- }
103
- };
76
+ 'src/common/guards/auth.guard.ts': `import type { Context, MiddlewareHandler } from 'frootjs';
77
+ import { verifyJwt } from 'frootjs/jwt';
78
+
79
+ const JWT_SECRET = process.env.JWT_SECRET || 'froot-secret-key-change-in-production';
80
+
81
+ export interface AuthContextVariables {
82
+ user: {
83
+ id: string;
84
+ email: string;
85
+ role: string;
86
+ };
87
+ }
88
+
89
+ export const authGuard: MiddlewareHandler = async (c, next) => {
90
+ const authHeader = c.req.header('authorization');
91
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
92
+ return c.json({ error: 'Unauthorized: missing or invalid authorization header' }, 401);
93
+ }
94
+
95
+ const token = authHeader.slice(7).trim();
96
+ try {
97
+ const payload = await verifyJwt(token, JWT_SECRET);
98
+ c.set('user', payload);
99
+ await next();
100
+ } catch {
101
+ return c.json({ error: 'Unauthorized: token is invalid or expired' }, 401);
102
+ }
103
+ };
104
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
- }
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
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
- },
143
- JWT_SECRET,
144
- { expiresIn: 60 * 60 * 24 }
145
- );
146
- }
147
- }
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
+ },
143
+ JWT_SECRET,
144
+ { expiresIn: 60 * 60 * 24 }
145
+ );
146
+ }
147
+ }
148
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
- });
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
174
  `,
175
- 'src/modules/users/users.types.ts': `export interface UserProfile {
176
- id: string;
177
- name: string;
178
- email: string;
179
- role: string;
180
- }
175
+ 'src/modules/users/users.types.ts': `export interface UserProfile {
176
+ id: string;
177
+ name: string;
178
+ email: string;
179
+ role: string;
180
+ }
181
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
- if (!id) return undefined;
196
- return mockUsers.find((u) => u.id === id);
197
- }
198
- }
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
+ if (!id) return undefined;
196
+ return mockUsers.find((u) => u.id === id);
197
+ }
198
+ }
199
199
  `,
200
- 'src/modules/users/users.routes.ts': `import { Froot } from 'frootjs';
201
- import { UsersService } from './users.service.js';
202
- import { authGuard } from '../../common/guards/auth.guard.js';
203
-
204
- export const usersModule = new Froot();
205
-
206
- // Public route: list users
207
- usersModule.get('/users', (c) => {
208
- return c.json(UsersService.getAll());
209
- });
210
-
211
- // Protected route: user details (guarded by JWT)
212
- usersModule.get('/users/:id', authGuard, (c) => {
213
- const id = c.req.param('id');
214
- const user = UsersService.getById(id);
215
- if (!user) {
216
- return c.json({ error: 'User not found' }, 404);
217
- }
218
- return c.json(user);
219
- });
200
+ 'src/modules/users/users.routes.ts': `import { Froot } from 'frootjs';
201
+ import { UsersService } from './users.service.js';
202
+ import { authGuard } from '../../common/guards/auth.guard.js';
203
+
204
+ export const usersModule = new Froot();
205
+
206
+ // Public route: list users
207
+ usersModule.get('/users', (c) => {
208
+ return c.json(UsersService.getAll());
209
+ });
210
+
211
+ // Protected route: user details (guarded by JWT)
212
+ usersModule.get('/users/:id', authGuard, (c) => {
213
+ const id = c.req.param('id');
214
+ const user = UsersService.getById(id);
215
+ if (!user) {
216
+ return c.json({ error: 'User not found' }, 404);
217
+ }
218
+ return c.json(user);
219
+ });
220
220
  `,
221
- 'src/modules/products/products.types.ts': `export interface Product {
222
- id: string;
223
- title: string;
224
- price: number;
225
- inventory: number;
226
- }
221
+ 'src/modules/products/products.types.ts': `export interface Product {
222
+ id: string;
223
+ title: string;
224
+ price: number;
225
+ inventory: number;
226
+ }
227
227
  `,
228
- 'src/modules/products/products.service.ts': `import type { Product } from './products.types.js';
229
-
230
- const productsTable: Product[] = [
231
- { id: 'prod_1', title: 'High-Performance Microservice', price: 99.0, inventory: 50 },
232
- { id: 'prod_2', title: 'Real-Time Edge Gateway', price: 149.0, inventory: 25 },
233
- ];
234
-
235
- export class ProductsService {
236
- static list(): Product[] {
237
- return productsTable;
238
- }
239
-
240
- static find(id?: string): Product | undefined {
241
- if (!id) return undefined;
242
- return productsTable.find((p) => p.id === id);
243
- }
244
- }
228
+ 'src/modules/products/products.service.ts': `import type { Product } from './products.types.js';
229
+
230
+ const productsTable: Product[] = [
231
+ { id: 'prod_1', title: 'High-Performance Microservice', price: 99.0, inventory: 50 },
232
+ { id: 'prod_2', title: 'Real-Time Edge Gateway', price: 149.0, inventory: 25 },
233
+ ];
234
+
235
+ export class ProductsService {
236
+ static list(): Product[] {
237
+ return productsTable;
238
+ }
239
+
240
+ static find(id?: string): Product | undefined {
241
+ if (!id) return undefined;
242
+ return productsTable.find((p) => p.id === id);
243
+ }
244
+ }
245
245
  `,
246
- 'src/modules/products/products.routes.ts': `import { Froot } from 'frootjs';
247
- import { ProductsService } from './products.service.js';
248
-
249
- export const productsModule = new Froot();
250
-
251
- productsModule.get('/products', (c) => {
252
- return c.json(ProductsService.list());
253
- });
254
-
255
- productsModule.get('/products/:id', (c) => {
256
- const id = c.req.param('id');
257
- const product = ProductsService.find(id);
258
- if (!product) {
259
- return c.json({ error: 'Product not found' }, 404);
260
- }
261
- return c.json(product);
262
- });
246
+ 'src/modules/products/products.routes.ts': `import { Froot } from 'frootjs';
247
+ import { ProductsService } from './products.service.js';
248
+
249
+ export const productsModule = new Froot();
250
+
251
+ productsModule.get('/products', (c) => {
252
+ return c.json(ProductsService.list());
253
+ });
254
+
255
+ productsModule.get('/products/:id', (c) => {
256
+ const id = c.req.param('id');
257
+ const product = ProductsService.find(id);
258
+ if (!product) {
259
+ return c.json({ error: 'Product not found' }, 404);
260
+ }
261
+ return c.json(product);
262
+ });
263
263
  `,
264
- 'src/app.ts': `import { Froot } from 'frootjs';
265
- import { cors } from 'frootjs/cors';
266
- import { errorHandler } from './common/filters/error.filter.js';
267
- import { authModule } from './modules/auth/auth.routes.js';
268
- import { usersModule } from './modules/users/users.routes.js';
269
- import { productsModule } from './modules/products/products.routes.js';
270
-
271
- export const app = new Froot();
272
-
273
- // Global middleware & filters
274
- app.use(cors());
275
- app.onError(errorHandler);
276
-
277
- // Mount feature modules under /api prefix
278
- app.route('/api', authModule);
279
- app.route('/api', usersModule);
280
- app.route('/api', productsModule);
281
-
282
- // Root & Health check
283
- app.get('/', (c) => {
284
- return c.json({
285
- message: 'Welcome to Froot Modular API',
286
- health: '/health',
287
- });
288
- });
289
-
290
- app.get('/health', (c) => {
291
- return c.json({
292
- status: 'ok',
293
- uptime: process.uptime(),
294
- modules: ['auth', 'users', 'products'],
295
- });
296
- });
264
+ 'src/app.ts': `import { Froot } from 'frootjs';
265
+ import { cors } from 'frootjs/cors';
266
+ import { errorHandler } from './common/filters/error.filter.js';
267
+ import { authModule } from './modules/auth/auth.routes.js';
268
+ import { usersModule } from './modules/users/users.routes.js';
269
+ import { productsModule } from './modules/products/products.routes.js';
270
+
271
+ export const app = new Froot();
272
+
273
+ // Global middleware & filters
274
+ app.use(cors());
275
+ app.onError(errorHandler);
276
+
277
+ // Mount feature modules under /api prefix
278
+ app.route('/api', authModule);
279
+ app.route('/api', usersModule);
280
+ app.route('/api', productsModule);
281
+
282
+ // Root & Health check
283
+ app.get('/', (c) => {
284
+ return c.json({
285
+ message: 'Welcome to Froot Modular API',
286
+ health: '/health',
287
+ });
288
+ });
289
+
290
+ app.get('/health', (c) => {
291
+ return c.json({
292
+ status: 'ok',
293
+ uptime: process.uptime(),
294
+ modules: ['auth', 'users', 'products'],
295
+ });
296
+ });
297
297
  `,
298
- 'src/server.ts': `import { app } from './app.js';
299
-
300
- const PORT = Number(process.env.PORT) || 3000;
301
-
302
- app.listen(PORT, () => {
303
- console.log(\`Modular Froot Server running on http://localhost:\${PORT}\`);
304
- });
298
+ 'src/server.ts': `import { app } from './app.js';
299
+
300
+ const PORT = Number(process.env.PORT) || 3000;
301
+
302
+ app.listen(PORT, () => {
303
+ console.log(\`Modular Froot Server running on http://localhost:\${PORT}\`);
304
+ });
305
305
  `,
306
306
  };
307
307
  },