@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.
- package/lib/index.js +434 -286
- 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.
|
|
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 {
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
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(
|
|
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.
|
|
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
|
|
1550
|
-
|
|
1551
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
export
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
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.
|
|
1585
|
-
import { users, posts, tasks } from '
|
|
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
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
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
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
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.
|
|
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
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
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
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
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
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
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
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
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
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
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.
|
|
1667
|
-
import {
|
|
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
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
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
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
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
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
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
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
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.
|
|
1737
|
-
import {
|
|
1738
|
-
import {
|
|
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
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
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
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
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
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
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
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
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
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
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
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
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.
|
|
1821
|
-
import {
|
|
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
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
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.
|
|
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
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
)
|
|
1866
|
-
|
|
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.
|
|
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
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"fs-extra": "^11.
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
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
|
+
}
|