@kozojs/cli 0.1.20 → 0.1.21

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.
Files changed (2) hide show
  1. package/lib/index.js +434 -286
  2. package/package.json +51 -51
package/lib/index.js CHANGED
@@ -1494,7 +1494,6 @@ ${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
1494
1494
  await import_fs_extra.default.writeJSON(import_node_path.default.join(apiDir, "tsconfig.json"), tsconfig, { spaces: 2 });
1495
1495
  const authImport = auth ? `import { authenticateJWT } from '@kozojs/auth';
1496
1496
  ` : "";
1497
- const servicesSetup = "\nconst services = {};\n";
1498
1497
  const authMiddleware = auth ? `
1499
1498
  // JWT protects all /api/* routes except public ones
1500
1499
  const JWT_SECRET = process.env.JWT_SECRET || 'change-me';
@@ -1505,21 +1504,22 @@ app.getApp().use('/api/*', (c, next) => {
1505
1504
  return _jwt(c, next);
1506
1505
  });
1507
1506
  ` : "";
1508
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "index.ts"), `import 'dotenv/config';
1507
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "index.ts"), `import 'dotenv/config';
1509
1508
  import { createKozo } from '@kozojs/core';
1510
- ${authImport}import { registerRoutes } from './routes/index.js';
1511
- ${servicesSetup}
1512
- const app = createKozo({ port: 3000, services });
1513
- ${authMiddleware}
1514
- registerRoutes(app);
1509
+ ${authImport}import { fileURLToPath } from 'node:url';
1510
+ import { dirname, join } from 'node:path';
1511
+
1512
+ const __dirname = dirname(fileURLToPath(import.meta.url));
1513
+ const PORT = Number(process.env.PORT) || 3000;
1514
+ const app = createKozo({ port: PORT });
1515
+ ${authMiddleware}await app.loadRoutes(join(__dirname, 'routes'));
1515
1516
 
1516
1517
  export type AppType = typeof app;
1517
1518
 
1518
- console.log('\u{1F525} ${projectName} API starting on http://localhost:3000');
1519
- console.log('\u{1F4DA} Endpoints: /api/health, /api/users, /api/posts, /api/tasks, /api/stats');
1519
+ console.log(\`\u{1F525} ${projectName} API on http://localhost:\${PORT}\`);
1520
1520
  await app.listen();
1521
1521
  `);
1522
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "data", "index.ts"), `import { z } from 'zod';
1522
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "schemas", "index.ts"), `import { z } from 'zod';
1523
1523
 
1524
1524
  export const UserSchema = z.object({
1525
1525
  id: z.string(),
@@ -1529,6 +1529,18 @@ export const UserSchema = z.object({
1529
1529
  createdAt: z.string().optional(),
1530
1530
  });
1531
1531
 
1532
+ export const CreateUserBody = z.object({
1533
+ name: z.string().min(1),
1534
+ email: z.string().email(),
1535
+ role: z.enum(['admin', 'user']).optional(),
1536
+ });
1537
+
1538
+ export const UpdateUserBody = z.object({
1539
+ name: z.string().optional(),
1540
+ email: z.string().email().optional(),
1541
+ role: z.enum(['admin', 'user']).optional(),
1542
+ });
1543
+
1532
1544
  export const PostSchema = z.object({
1533
1545
  id: z.string(),
1534
1546
  title: z.string(),
@@ -1538,6 +1550,19 @@ export const PostSchema = z.object({
1538
1550
  createdAt: z.string().optional(),
1539
1551
  });
1540
1552
 
1553
+ export const CreatePostBody = z.object({
1554
+ title: z.string().min(1),
1555
+ content: z.string().optional(),
1556
+ authorId: z.string().optional(),
1557
+ published: z.boolean().optional(),
1558
+ });
1559
+
1560
+ export const UpdatePostBody = z.object({
1561
+ title: z.string().optional(),
1562
+ content: z.string().optional(),
1563
+ published: z.boolean().optional(),
1564
+ });
1565
+
1541
1566
  export const TaskSchema = z.object({
1542
1567
  id: z.string(),
1543
1568
  title: z.string(),
@@ -1546,302 +1571,415 @@ export const TaskSchema = z.object({
1546
1571
  createdAt: z.string(),
1547
1572
  });
1548
1573
 
1549
- export const users: z.infer<typeof UserSchema>[] = [
1550
- { id: '1', name: 'Alice', email: 'alice@example.com', role: 'admin', createdAt: new Date().toISOString() },
1551
- { id: '2', name: 'Bob', email: 'bob@example.com', role: 'user', createdAt: new Date().toISOString() },
1574
+ export const CreateTaskBody = z.object({
1575
+ title: z.string().min(1),
1576
+ priority: z.enum(['low', 'medium', 'high']).optional(),
1577
+ });
1578
+
1579
+ export const UpdateTaskBody = z.object({
1580
+ title: z.string().optional(),
1581
+ completed: z.boolean().optional(),
1582
+ priority: z.enum(['low', 'medium', 'high']).optional(),
1583
+ });
1584
+ `);
1585
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "data", "index.ts"), `export const users = [
1586
+ { id: '1', name: 'Alice', email: 'alice@example.com', role: 'admin' as const, createdAt: new Date().toISOString() },
1587
+ { id: '2', name: 'Bob', email: 'bob@example.com', role: 'user' as const, createdAt: new Date().toISOString() },
1552
1588
  ];
1553
1589
 
1554
- export const posts: z.infer<typeof PostSchema>[] = [
1590
+ export const posts = [
1555
1591
  { id: '1', title: 'Hello World', content: 'First post!', authorId: '1', published: true, createdAt: new Date().toISOString() },
1556
1592
  { id: '2', title: 'Draft', content: 'Work in progress', authorId: '2', published: false, createdAt: new Date().toISOString() },
1557
1593
  ];
1558
1594
 
1559
- export const tasks: z.infer<typeof TaskSchema>[] = [
1560
- { id: '1', title: 'Setup project', completed: true, priority: 'high', createdAt: new Date().toISOString() },
1561
- { id: '2', title: 'Write tests', completed: false, priority: 'medium', createdAt: new Date().toISOString() },
1562
- { id: '3', title: 'Deploy', completed: false, priority: 'low', createdAt: new Date().toISOString() },
1595
+ export const tasks = [
1596
+ { id: '1', title: 'Setup project', completed: true, priority: 'high' as const, createdAt: new Date().toISOString() },
1597
+ { id: '2', title: 'Write tests', completed: false, priority: 'medium' as const, createdAt: new Date().toISOString() },
1598
+ { id: '3', title: 'Deploy', completed: false, priority: 'low' as const, createdAt: new Date().toISOString() },
1563
1599
  ];
1564
1600
  `);
1565
- const authRoutesImport = auth ? `import { registerAuthRoutes } from './auth';
1566
- ` : "";
1567
- const authRoutesCall = auth ? ` registerAuthRoutes(app); // Public auth endpoint must register before JWT middleware
1568
- ` : "";
1569
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "routes", "index.ts"), `import type { Kozo } from '@kozojs/core';
1570
- import { registerHealthRoutes } from './health';
1571
- import { registerUserRoutes } from './users';
1572
- import { registerPostRoutes } from './posts';
1573
- import { registerTaskRoutes } from './tasks';
1574
- import { registerToolRoutes } from './tools';
1575
- ${authRoutesImport}
1576
- export function registerRoutes(app: Kozo) {
1577
- ${authRoutesCall} registerHealthRoutes(app);
1578
- registerUserRoutes(app);
1579
- registerPostRoutes(app);
1580
- registerTaskRoutes(app);
1581
- registerToolRoutes(app);
1582
- }
1601
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "health", "get.ts"), `import { z } from 'zod';
1602
+
1603
+ export const schema = {
1604
+ response: z.object({
1605
+ status: z.string(),
1606
+ timestamp: z.string(),
1607
+ version: z.string(),
1608
+ uptime: z.number(),
1609
+ }),
1610
+ };
1611
+
1612
+ export default async () => ({
1613
+ status: 'ok',
1614
+ timestamp: new Date().toISOString(),
1615
+ version: '1.0.0',
1616
+ uptime: process.uptime(),
1617
+ });
1583
1618
  `);
1584
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "routes", "health.ts"), `import type { Kozo } from '@kozojs/core';
1585
- import { users, posts, tasks } from '../data';
1619
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "stats", "get.ts"), `import { z } from 'zod';
1620
+ import { users, posts, tasks } from '../../../data/index.js';
1586
1621
 
1587
- export function registerHealthRoutes(app: Kozo) {
1588
- app.get('/api/health', {}, () => ({
1589
- status: 'ok',
1590
- timestamp: new Date().toISOString(),
1591
- version: '1.0.0',
1592
- uptime: process.uptime(),
1593
- }));
1622
+ export const schema = {
1623
+ response: z.object({
1624
+ users: z.number(),
1625
+ posts: z.number(),
1626
+ tasks: z.number(),
1627
+ publishedPosts: z.number(),
1628
+ completedTasks: z.number(),
1629
+ }),
1630
+ };
1594
1631
 
1595
- app.get('/api/stats', {}, () => ({
1596
- users: users.length,
1597
- posts: posts.length,
1598
- tasks: tasks.length,
1599
- publishedPosts: posts.filter(p => p.published).length,
1600
- completedTasks: tasks.filter(t => t.completed).length,
1601
- }));
1602
- }
1632
+ export default async () => ({
1633
+ users: users.length,
1634
+ posts: posts.length,
1635
+ tasks: tasks.length,
1636
+ publishedPosts: posts.filter(p => p.published).length,
1637
+ completedTasks: tasks.filter(t => t.completed).length,
1638
+ });
1603
1639
  `);
1604
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "routes", "users.ts"), `import type { Kozo } from '@kozojs/core';
1605
- import { z } from 'zod';
1606
- import { users, UserSchema } from '../data';
1640
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "echo", "get.ts"), `import { z } from 'zod';
1607
1641
 
1608
- export function registerUserRoutes(app: Kozo) {
1609
- app.get('/api/users', {
1610
- response: z.array(UserSchema),
1611
- }, () => users);
1642
+ export const schema = {
1643
+ query: z.object({ message: z.string() }),
1644
+ response: z.object({
1645
+ echo: z.string(),
1646
+ timestamp: z.string(),
1647
+ }),
1648
+ };
1612
1649
 
1613
- app.get('/api/users/:id', {
1614
- params: z.object({ id: z.string() }),
1615
- response: UserSchema,
1616
- }, (c) => {
1617
- const user = users.find(u => u.id === c.params.id);
1618
- if (!user) throw new Error('User not found');
1619
- return user;
1620
- });
1650
+ export default async ({ query }: { query: { message: string } }) => ({
1651
+ echo: query.message,
1652
+ timestamp: new Date().toISOString(),
1653
+ });
1654
+ `);
1655
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "validate", "post.ts"), `import { z } from 'zod';
1621
1656
 
1622
- app.post('/api/users', {
1623
- body: z.object({
1624
- name: z.string().min(1),
1625
- email: z.string().email(),
1626
- role: z.enum(['admin', 'user']).optional(),
1627
- }),
1628
- response: UserSchema,
1629
- }, (c) => {
1630
- const newUser = {
1631
- id: String(Date.now()),
1632
- name: c.body.name,
1633
- email: c.body.email,
1634
- role: c.body.role || 'user' as const,
1635
- createdAt: new Date().toISOString(),
1636
- };
1637
- users.push(newUser);
1638
- return newUser;
1639
- });
1657
+ export const schema = {
1658
+ body: z.object({
1659
+ email: z.string().email(),
1660
+ age: z.number().min(0).max(150),
1661
+ }),
1662
+ response: z.object({
1663
+ valid: z.boolean(),
1664
+ data: z.object({ email: z.string(), age: z.number() }),
1665
+ }),
1666
+ };
1640
1667
 
1641
- app.put('/api/users/:id', {
1642
- params: z.object({ id: z.string() }),
1643
- body: z.object({
1644
- name: z.string().min(1).optional(),
1645
- email: z.string().email().optional(),
1646
- role: z.enum(['admin', 'user']).optional(),
1647
- }),
1648
- response: UserSchema,
1649
- }, (c) => {
1650
- const idx = users.findIndex(u => u.id === c.params.id);
1651
- if (idx === -1) throw new Error('User not found');
1652
- users[idx] = { ...users[idx], ...c.body };
1653
- return users[idx];
1654
- });
1668
+ export default async ({ body }: { body: { email: string; age: number } }) => ({
1669
+ valid: true,
1670
+ data: body,
1671
+ });
1672
+ `);
1673
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "users", "get.ts"), `import { z } from 'zod';
1674
+ import { users } from '../../../data/index.js';
1675
+ import { UserSchema } from '../../../schemas/index.js';
1655
1676
 
1656
- app.delete('/api/users/:id', {
1657
- params: z.object({ id: z.string() }),
1658
- }, (c) => {
1659
- const idx = users.findIndex(u => u.id === c.params.id);
1660
- if (idx === -1) throw new Error('User not found');
1661
- users.splice(idx, 1);
1662
- return { success: true, message: 'User deleted' };
1663
- });
1664
- }
1677
+ export const schema = {
1678
+ response: z.array(UserSchema),
1679
+ };
1680
+
1681
+ export default async () => users;
1665
1682
  `);
1666
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "routes", "posts.ts"), `import type { Kozo } from '@kozojs/core';
1667
- import { z } from 'zod';
1668
- import { posts, PostSchema } from '../data';
1683
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "users", "post.ts"), `import { users } from '../../../data/index.js';
1684
+ import { UserSchema, CreateUserBody } from '../../../schemas/index.js';
1669
1685
 
1670
- export function registerPostRoutes(app: Kozo) {
1671
- app.get('/api/posts', {
1672
- query: z.object({ published: z.coerce.boolean().optional() }),
1673
- response: z.array(PostSchema),
1674
- }, (c) => {
1675
- if (c.query.published !== undefined) {
1676
- return posts.filter(p => p.published === c.query.published);
1677
- }
1678
- return posts;
1679
- });
1686
+ export const schema = {
1687
+ body: CreateUserBody,
1688
+ response: UserSchema,
1689
+ };
1680
1690
 
1681
- app.get('/api/posts/:id', {
1682
- params: z.object({ id: z.string() }),
1683
- response: PostSchema,
1684
- }, (c) => {
1685
- const post = posts.find(p => p.id === c.params.id);
1686
- if (!post) throw new Error('Post not found');
1687
- return post;
1688
- });
1691
+ export default async ({ body }: { body: { name: string; email: string; role?: 'admin' | 'user' } }) => {
1692
+ const newUser = {
1693
+ id: String(Date.now()),
1694
+ name: body.name,
1695
+ email: body.email,
1696
+ role: body.role ?? ('user' as const),
1697
+ createdAt: new Date().toISOString(),
1698
+ };
1699
+ users.push(newUser);
1700
+ return newUser;
1701
+ };
1702
+ `);
1703
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "users", "[id]", "get.ts"), `import { z } from 'zod';
1704
+ import { KozoError } from '@kozojs/core';
1705
+ import { users } from '../../../../data/index.js';
1706
+ import { UserSchema } from '../../../../schemas/index.js';
1689
1707
 
1690
- app.post('/api/posts', {
1691
- body: z.object({
1692
- title: z.string().min(1),
1693
- content: z.string(),
1694
- authorId: z.string(),
1695
- published: z.boolean().optional(),
1696
- }),
1697
- response: PostSchema,
1698
- }, (c) => {
1699
- const newPost = {
1700
- id: String(Date.now()),
1701
- title: c.body.title,
1702
- content: c.body.content,
1703
- authorId: c.body.authorId,
1704
- published: c.body.published ?? false,
1705
- createdAt: new Date().toISOString(),
1706
- };
1707
- posts.push(newPost);
1708
- return newPost;
1709
- });
1708
+ export const schema = {
1709
+ params: z.object({ id: z.string() }),
1710
+ response: UserSchema,
1711
+ };
1710
1712
 
1711
- app.put('/api/posts/:id', {
1712
- params: z.object({ id: z.string() }),
1713
- body: z.object({
1714
- title: z.string().min(1).optional(),
1715
- content: z.string().optional(),
1716
- published: z.boolean().optional(),
1717
- }),
1718
- response: PostSchema,
1719
- }, (c) => {
1720
- const idx = posts.findIndex(p => p.id === c.params.id);
1721
- if (idx === -1) throw new Error('Post not found');
1722
- posts[idx] = { ...posts[idx], ...c.body };
1723
- return posts[idx];
1724
- });
1713
+ export default async ({ params }: { params: { id: string } }) => {
1714
+ const user = users.find(u => u.id === params.id);
1715
+ if (!user) throw new KozoError('User not found', 404, 'NOT_FOUND');
1716
+ return user;
1717
+ };
1718
+ `);
1719
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "users", "[id]", "put.ts"), `import { z } from 'zod';
1720
+ import { KozoError } from '@kozojs/core';
1721
+ import { users } from '../../../../data/index.js';
1722
+ import { UserSchema, UpdateUserBody } from '../../../../schemas/index.js';
1725
1723
 
1726
- app.delete('/api/posts/:id', {
1727
- params: z.object({ id: z.string() }),
1728
- }, (c) => {
1729
- const idx = posts.findIndex(p => p.id === c.params.id);
1730
- if (idx === -1) throw new Error('Post not found');
1731
- posts.splice(idx, 1);
1732
- return { success: true, message: 'Post deleted' };
1733
- });
1734
- }
1724
+ export const schema = {
1725
+ params: z.object({ id: z.string() }),
1726
+ body: UpdateUserBody,
1727
+ response: UserSchema,
1728
+ };
1729
+
1730
+ export default async ({
1731
+ params,
1732
+ body,
1733
+ }: {
1734
+ params: { id: string };
1735
+ body: { name?: string; email?: string; role?: 'admin' | 'user' };
1736
+ }) => {
1737
+ const idx = users.findIndex(u => u.id === params.id);
1738
+ if (idx === -1) throw new KozoError('User not found', 404, 'NOT_FOUND');
1739
+ users[idx] = { ...users[idx], ...body };
1740
+ return users[idx];
1741
+ };
1735
1742
  `);
1736
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "routes", "tasks.ts"), `import type { Kozo } from '@kozojs/core';
1737
- import { z } from 'zod';
1738
- import { tasks, TaskSchema } from '../data';
1743
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "users", "[id]", "delete.ts"), `import { z } from 'zod';
1744
+ import { KozoError } from '@kozojs/core';
1745
+ import { users } from '../../../../data/index.js';
1739
1746
 
1740
- export function registerTaskRoutes(app: Kozo) {
1741
- app.get('/api/tasks', {
1742
- query: z.object({
1743
- completed: z.coerce.boolean().optional(),
1744
- priority: z.enum(['low', 'medium', 'high']).optional(),
1745
- }),
1746
- response: z.array(TaskSchema),
1747
- }, (c) => {
1748
- let result = [...tasks];
1749
- if (c.query.completed !== undefined) {
1750
- result = result.filter(t => t.completed === c.query.completed);
1751
- }
1752
- if (c.query.priority) {
1753
- result = result.filter(t => t.priority === c.query.priority);
1754
- }
1755
- return result;
1756
- });
1747
+ export const schema = {
1748
+ params: z.object({ id: z.string() }),
1749
+ response: z.object({ success: z.boolean(), message: z.string() }),
1750
+ };
1757
1751
 
1758
- app.get('/api/tasks/:id', {
1759
- params: z.object({ id: z.string() }),
1760
- response: TaskSchema,
1761
- }, (c) => {
1762
- const task = tasks.find(t => t.id === c.params.id);
1763
- if (!task) throw new Error('Task not found');
1764
- return task;
1765
- });
1752
+ export default async ({ params }: { params: { id: string } }) => {
1753
+ const idx = users.findIndex(u => u.id === params.id);
1754
+ if (idx === -1) throw new KozoError('User not found', 404, 'NOT_FOUND');
1755
+ users.splice(idx, 1);
1756
+ return { success: true, message: 'User deleted' };
1757
+ };
1758
+ `);
1759
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "posts", "get.ts"), `import { z } from 'zod';
1760
+ import { posts } from '../../../data/index.js';
1761
+ import { PostSchema } from '../../../schemas/index.js';
1766
1762
 
1767
- app.post('/api/tasks', {
1768
- body: z.object({
1769
- title: z.string().min(1),
1770
- priority: z.enum(['low', 'medium', 'high']).optional(),
1771
- }),
1772
- response: TaskSchema,
1773
- }, (c) => {
1774
- const newTask = {
1775
- id: String(Date.now()),
1776
- title: c.body.title,
1777
- completed: false,
1778
- priority: c.body.priority || 'medium' as const,
1779
- createdAt: new Date().toISOString(),
1780
- };
1781
- tasks.push(newTask);
1782
- return newTask;
1783
- });
1763
+ export const schema = {
1764
+ query: z.object({ published: z.coerce.boolean().optional() }),
1765
+ response: z.array(PostSchema),
1766
+ };
1784
1767
 
1785
- app.put('/api/tasks/:id', {
1786
- params: z.object({ id: z.string() }),
1787
- body: z.object({
1788
- title: z.string().min(1).optional(),
1789
- completed: z.boolean().optional(),
1790
- priority: z.enum(['low', 'medium', 'high']).optional(),
1791
- }),
1792
- response: TaskSchema,
1793
- }, (c) => {
1794
- const idx = tasks.findIndex(t => t.id === c.params.id);
1795
- if (idx === -1) throw new Error('Task not found');
1796
- tasks[idx] = { ...tasks[idx], ...c.body };
1797
- return tasks[idx];
1798
- });
1768
+ export default async ({ query }: { query: { published?: boolean } }) => {
1769
+ if (query.published !== undefined) {
1770
+ return posts.filter(p => p.published === query.published);
1771
+ }
1772
+ return posts;
1773
+ };
1774
+ `);
1775
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "posts", "post.ts"), `import { posts, users } from '../../../data/index.js';
1776
+ import { PostSchema, CreatePostBody } from '../../../schemas/index.js';
1799
1777
 
1800
- app.patch('/api/tasks/:id/toggle', {
1801
- params: z.object({ id: z.string() }),
1802
- response: TaskSchema,
1803
- }, (c) => {
1804
- const idx = tasks.findIndex(t => t.id === c.params.id);
1805
- if (idx === -1) throw new Error('Task not found');
1806
- tasks[idx].completed = !tasks[idx].completed;
1807
- return tasks[idx];
1808
- });
1778
+ export const schema = {
1779
+ body: CreatePostBody,
1780
+ response: PostSchema,
1781
+ };
1809
1782
 
1810
- app.delete('/api/tasks/:id', {
1811
- params: z.object({ id: z.string() }),
1812
- }, (c) => {
1813
- const idx = tasks.findIndex(t => t.id === c.params.id);
1814
- if (idx === -1) throw new Error('Task not found');
1815
- tasks.splice(idx, 1);
1816
- return { success: true, message: 'Task deleted' };
1817
- });
1818
- }
1783
+ export default async ({
1784
+ body,
1785
+ }: {
1786
+ body: { title: string; content?: string; authorId?: string; published?: boolean };
1787
+ }) => {
1788
+ const authorId = body.authorId ?? users[0]?.id ?? 'unknown';
1789
+ const newPost = {
1790
+ id: String(Date.now()),
1791
+ title: body.title,
1792
+ content: body.content ?? '',
1793
+ authorId,
1794
+ published: body.published ?? false,
1795
+ createdAt: new Date().toISOString(),
1796
+ };
1797
+ posts.push(newPost);
1798
+ return newPost;
1799
+ };
1819
1800
  `);
1820
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "routes", "tools.ts"), `import type { Kozo } from '@kozojs/core';
1821
- import { z } from 'zod';
1801
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "posts", "[id]", "get.ts"), `import { z } from 'zod';
1802
+ import { KozoError } from '@kozojs/core';
1803
+ import { posts } from '../../../../data/index.js';
1804
+ import { PostSchema } from '../../../../schemas/index.js';
1822
1805
 
1823
- export function registerToolRoutes(app: Kozo) {
1824
- app.get('/api/echo', {
1825
- query: z.object({ message: z.string() }),
1826
- }, (c) => ({
1827
- echo: c.query.message,
1828
- timestamp: new Date().toISOString(),
1829
- }));
1806
+ export const schema = {
1807
+ params: z.object({ id: z.string() }),
1808
+ response: PostSchema,
1809
+ };
1830
1810
 
1831
- app.post('/api/validate', {
1832
- body: z.object({
1833
- email: z.string().email(),
1834
- age: z.number().min(0).max(150),
1835
- }),
1836
- }, (c) => ({
1837
- valid: true,
1838
- data: c.body,
1839
- }));
1840
- }
1811
+ export default async ({ params }: { params: { id: string } }) => {
1812
+ const post = posts.find(p => p.id === params.id);
1813
+ if (!post) throw new KozoError('Post not found', 404, 'NOT_FOUND');
1814
+ return post;
1815
+ };
1816
+ `);
1817
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "posts", "[id]", "put.ts"), `import { z } from 'zod';
1818
+ import { KozoError } from '@kozojs/core';
1819
+ import { posts } from '../../../../data/index.js';
1820
+ import { PostSchema, UpdatePostBody } from '../../../../schemas/index.js';
1821
+
1822
+ export const schema = {
1823
+ params: z.object({ id: z.string() }),
1824
+ body: UpdatePostBody,
1825
+ response: PostSchema,
1826
+ };
1827
+
1828
+ export default async ({
1829
+ params,
1830
+ body,
1831
+ }: {
1832
+ params: { id: string };
1833
+ body: { title?: string; content?: string; published?: boolean };
1834
+ }) => {
1835
+ const idx = posts.findIndex(p => p.id === params.id);
1836
+ if (idx === -1) throw new KozoError('Post not found', 404, 'NOT_FOUND');
1837
+ posts[idx] = { ...posts[idx], ...body };
1838
+ return posts[idx];
1839
+ };
1840
+ `);
1841
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "posts", "[id]", "delete.ts"), `import { z } from 'zod';
1842
+ import { KozoError } from '@kozojs/core';
1843
+ import { posts } from '../../../../data/index.js';
1844
+
1845
+ export const schema = {
1846
+ params: z.object({ id: z.string() }),
1847
+ response: z.object({ success: z.boolean(), message: z.string() }),
1848
+ };
1849
+
1850
+ export default async ({ params }: { params: { id: string } }) => {
1851
+ const idx = posts.findIndex(p => p.id === params.id);
1852
+ if (idx === -1) throw new KozoError('Post not found', 404, 'NOT_FOUND');
1853
+ posts.splice(idx, 1);
1854
+ return { success: true, message: 'Post deleted' };
1855
+ };
1856
+ `);
1857
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "tasks", "get.ts"), `import { z } from 'zod';
1858
+ import { tasks } from '../../../data/index.js';
1859
+ import { TaskSchema } from '../../../schemas/index.js';
1860
+
1861
+ export const schema = {
1862
+ query: z.object({
1863
+ completed: z.coerce.boolean().optional(),
1864
+ priority: z.enum(['low', 'medium', 'high']).optional(),
1865
+ }),
1866
+ response: z.array(TaskSchema),
1867
+ };
1868
+
1869
+ export default async ({
1870
+ query,
1871
+ }: {
1872
+ query: { completed?: boolean; priority?: 'low' | 'medium' | 'high' };
1873
+ }) => {
1874
+ let result = [...tasks];
1875
+ if (query.completed !== undefined) {
1876
+ result = result.filter(t => t.completed === query.completed);
1877
+ }
1878
+ if (query.priority) {
1879
+ result = result.filter(t => t.priority === query.priority);
1880
+ }
1881
+ return result;
1882
+ };
1883
+ `);
1884
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "tasks", "post.ts"), `import { tasks } from '../../../data/index.js';
1885
+ import { TaskSchema, CreateTaskBody } from '../../../schemas/index.js';
1886
+
1887
+ export const schema = {
1888
+ body: CreateTaskBody,
1889
+ response: TaskSchema,
1890
+ };
1891
+
1892
+ export default async ({
1893
+ body,
1894
+ }: {
1895
+ body: { title: string; priority?: 'low' | 'medium' | 'high' };
1896
+ }) => {
1897
+ const newTask = {
1898
+ id: String(Date.now()),
1899
+ title: body.title,
1900
+ completed: false,
1901
+ priority: body.priority ?? ('medium' as const),
1902
+ createdAt: new Date().toISOString(),
1903
+ };
1904
+ tasks.push(newTask);
1905
+ return newTask;
1906
+ };
1907
+ `);
1908
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "tasks", "[id]", "get.ts"), `import { z } from 'zod';
1909
+ import { KozoError } from '@kozojs/core';
1910
+ import { tasks } from '../../../../data/index.js';
1911
+ import { TaskSchema } from '../../../../schemas/index.js';
1912
+
1913
+ export const schema = {
1914
+ params: z.object({ id: z.string() }),
1915
+ response: TaskSchema,
1916
+ };
1917
+
1918
+ export default async ({ params }: { params: { id: string } }) => {
1919
+ const task = tasks.find(t => t.id === params.id);
1920
+ if (!task) throw new KozoError('Task not found', 404, 'NOT_FOUND');
1921
+ return task;
1922
+ };
1923
+ `);
1924
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "tasks", "[id]", "put.ts"), `import { z } from 'zod';
1925
+ import { KozoError } from '@kozojs/core';
1926
+ import { tasks } from '../../../../data/index.js';
1927
+ import { TaskSchema, UpdateTaskBody } from '../../../../schemas/index.js';
1928
+
1929
+ export const schema = {
1930
+ params: z.object({ id: z.string() }),
1931
+ body: UpdateTaskBody,
1932
+ response: TaskSchema,
1933
+ };
1934
+
1935
+ export default async ({
1936
+ params,
1937
+ body,
1938
+ }: {
1939
+ params: { id: string };
1940
+ body: { title?: string; completed?: boolean; priority?: 'low' | 'medium' | 'high' };
1941
+ }) => {
1942
+ const idx = tasks.findIndex(t => t.id === params.id);
1943
+ if (idx === -1) throw new KozoError('Task not found', 404, 'NOT_FOUND');
1944
+ tasks[idx] = { ...tasks[idx], ...body };
1945
+ return tasks[idx];
1946
+ };
1947
+ `);
1948
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "tasks", "[id]", "delete.ts"), `import { z } from 'zod';
1949
+ import { KozoError } from '@kozojs/core';
1950
+ import { tasks } from '../../../../data/index.js';
1951
+
1952
+ export const schema = {
1953
+ params: z.object({ id: z.string() }),
1954
+ response: z.object({ success: z.boolean(), message: z.string() }),
1955
+ };
1956
+
1957
+ export default async ({ params }: { params: { id: string } }) => {
1958
+ const idx = tasks.findIndex(t => t.id === params.id);
1959
+ if (idx === -1) throw new KozoError('Task not found', 404, 'NOT_FOUND');
1960
+ tasks.splice(idx, 1);
1961
+ return { success: true, message: 'Task deleted' };
1962
+ };
1963
+ `);
1964
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "tasks", "[id]", "toggle", "patch.ts"), `import { z } from 'zod';
1965
+ import { KozoError } from '@kozojs/core';
1966
+ import { tasks } from '../../../../../data/index.js';
1967
+ import { TaskSchema } from '../../../../../schemas/index.js';
1968
+
1969
+ export const schema = {
1970
+ params: z.object({ id: z.string() }),
1971
+ response: TaskSchema,
1972
+ };
1973
+
1974
+ export default async ({ params }: { params: { id: string } }) => {
1975
+ const idx = tasks.findIndex(t => t.id === params.id);
1976
+ if (idx === -1) throw new KozoError('Task not found', 404, 'NOT_FOUND');
1977
+ tasks[idx].completed = !tasks[idx].completed;
1978
+ return tasks[idx];
1979
+ };
1841
1980
  `);
1842
1981
  if (auth) {
1843
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "routes", "auth.ts"), `import type { Kozo } from '@kozojs/core';
1844
- import { z } from 'zod';
1982
+ await import_fs_extra.default.outputFile(import_node_path.default.join(apiDir, "src", "routes", "api", "auth", "login", "post.ts"), `import { z } from 'zod';
1845
1983
  import { createJWT, UnauthorizedError } from '@kozojs/auth';
1846
1984
 
1847
1985
  const JWT_SECRET = process.env.JWT_SECRET || 'change-me';
@@ -1851,21 +1989,31 @@ const DEMO_USERS = [
1851
1989
  { email: 'user@demo.com', password: 'user123', role: 'user', name: 'User' },
1852
1990
  ];
1853
1991
 
1854
- export function registerAuthRoutes(app: Kozo) {
1855
- app.post('/api/auth/login', {
1856
- body: z.object({ email: z.string().email(), password: z.string() }),
1857
- }, async (c) => {
1858
- const { email, password } = c.body;
1859
- const user = DEMO_USERS.find(u => u.email === email && u.password === password);
1860
- if (!user) throw new UnauthorizedError('Invalid credentials');
1861
- const token = await createJWT(
1862
- { email: user.email, role: user.role, name: user.name },
1863
- JWT_SECRET,
1864
- { expiresIn: '24h' },
1865
- );
1866
- return { token, user: { email: user.email, role: user.role, name: user.name } };
1867
- });
1868
- }
1992
+ export const schema = {
1993
+ body: z.object({
1994
+ email: z.string().email(),
1995
+ password: z.string(),
1996
+ }),
1997
+ response: z.object({
1998
+ token: z.string(),
1999
+ user: z.object({
2000
+ email: z.string(),
2001
+ role: z.string(),
2002
+ name: z.string(),
2003
+ }),
2004
+ }),
2005
+ };
2006
+
2007
+ export default async ({ body }: { body: { email: string; password: string } }) => {
2008
+ const user = DEMO_USERS.find(u => u.email === body.email && u.password === body.password);
2009
+ if (!user) throw new UnauthorizedError('Invalid credentials');
2010
+ const token = await createJWT(
2011
+ { email: user.email, role: user.role, name: user.name },
2012
+ JWT_SECRET,
2013
+ { expiresIn: '24h' },
2014
+ );
2015
+ return { token, user: { email: user.email, role: user.role, name: user.name } };
2016
+ };
1869
2017
  `);
1870
2018
  }
1871
2019
  }
package/package.json CHANGED
@@ -1,51 +1,51 @@
1
- {
2
- "name": "@kozojs/cli",
3
- "version": "0.1.20",
4
- "description": "CLI to scaffold new Kozo Framework projects - The next-gen TypeScript Backend Framework",
5
- "bin": {
6
- "create-kozo": "./lib/index.js",
7
- "kozo": "./lib/index.js"
8
- },
9
- "main": "./lib/index.js",
10
- "types": "./lib/index.d.ts",
11
- "files": [
12
- "lib",
13
- "README.md"
14
- ],
15
- "scripts": {
16
- "build": "tsup",
17
- "dev": "tsup --watch"
18
- },
19
- "keywords": [
20
- "kozo",
21
- "framework",
22
- "backend",
23
- "typescript",
24
- "hono",
25
- "api",
26
- "rest",
27
- "cli",
28
- "scaffold",
29
- "generator"
30
- ],
31
- "author": "Kozo Team",
32
- "license": "MIT",
33
- "engines": {
34
- "node": ">=18.0.0"
35
- },
36
- "dependencies": {
37
- "@clack/prompts": "^0.8.0",
38
- "commander": "^12.0.0",
39
- "picocolors": "^1.1.0",
40
- "ora": "^8.1.0",
41
- "execa": "^9.5.0",
42
- "fs-extra": "^11.2.0",
43
- "glob": "^11.0.0"
44
- },
45
- "devDependencies": {
46
- "@types/fs-extra": "^11.0.4",
47
- "@types/node": "^22.0.0",
48
- "tsup": "^8.3.0",
49
- "typescript": "^5.6.0"
50
- }
51
- }
1
+ {
2
+ "name": "@kozojs/cli",
3
+ "version": "0.1.21",
4
+ "description": "CLI to scaffold new Kozo Framework projects - The next-gen TypeScript Backend Framework",
5
+ "bin": {
6
+ "create-kozo": "./lib/index.js",
7
+ "kozo": "./lib/index.js"
8
+ },
9
+ "main": "./lib/index.js",
10
+ "types": "./lib/index.d.ts",
11
+ "files": [
12
+ "lib",
13
+ "README.md"
14
+ ],
15
+ "keywords": [
16
+ "kozo",
17
+ "framework",
18
+ "backend",
19
+ "typescript",
20
+ "hono",
21
+ "api",
22
+ "rest",
23
+ "cli",
24
+ "scaffold",
25
+ "generator"
26
+ ],
27
+ "author": "Kozo Team",
28
+ "license": "MIT",
29
+ "engines": {
30
+ "node": ">=18.0.0"
31
+ },
32
+ "dependencies": {
33
+ "@clack/prompts": "^0.8.0",
34
+ "commander": "^12.0.0",
35
+ "picocolors": "^1.1.0",
36
+ "ora": "^8.1.0",
37
+ "execa": "^9.5.0",
38
+ "fs-extra": "^11.2.0",
39
+ "glob": "^11.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/fs-extra": "^11.0.4",
43
+ "@types/node": "^22.0.0",
44
+ "tsup": "^8.3.0",
45
+ "typescript": "^5.6.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "dev": "tsup --watch"
50
+ }
51
+ }