create-theokit 0.1.0-alpha.0
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/cli.js +118 -0
- package/dist/cli.js.map +1 -0
- package/package.json +19 -0
- package/templates/api-only/_gitignore +5 -0
- package/templates/api-only/app/page.tsx +3 -0
- package/templates/api-only/index.html +12 -0
- package/templates/api-only/package.json.tmpl +19 -0
- package/templates/api-only/public/.gitkeep +0 -0
- package/templates/api-only/server/routes/health.ts +5 -0
- package/templates/api-only/server/routes/users.ts +27 -0
- package/templates/api-only/theo.config.ts +3 -0
- package/templates/api-only/tsconfig.json +15 -0
- package/templates/dashboard/_gitignore +5 -0
- package/templates/dashboard/app/about/page.tsx +3 -0
- package/templates/dashboard/app/dashboard/layout.tsx +10 -0
- package/templates/dashboard/app/dashboard/page.tsx +3 -0
- package/templates/dashboard/app/layout.tsx +14 -0
- package/templates/dashboard/app/page.tsx +8 -0
- package/templates/dashboard/index.html +12 -0
- package/templates/dashboard/package.json.tmpl +19 -0
- package/templates/dashboard/public/.gitkeep +0 -0
- package/templates/dashboard/server/routes/health.ts +5 -0
- package/templates/dashboard/theo.config.ts +3 -0
- package/templates/dashboard/tsconfig.json +15 -0
- package/templates/default/_gitignore +5 -0
- package/templates/default/app/layout.tsx +3 -0
- package/templates/default/app/page.tsx +3 -0
- package/templates/default/index.html +12 -0
- package/templates/default/package.json.tmpl +19 -0
- package/templates/default/public/.gitkeep +0 -0
- package/templates/default/server/routes/health.ts +5 -0
- package/templates/default/theo.config.ts +3 -0
- package/templates/default/tsconfig.json +15 -0
- package/templates/postgres/.env.example +5 -0
- package/templates/postgres/_gitignore +5 -0
- package/templates/postgres/app/layout.tsx +14 -0
- package/templates/postgres/app/page.tsx +8 -0
- package/templates/postgres/db/index.ts +7 -0
- package/templates/postgres/db/schema.ts +8 -0
- package/templates/postgres/drizzle.config.ts +10 -0
- package/templates/postgres/index.html +12 -0
- package/templates/postgres/package.json.tmpl +29 -0
- package/templates/postgres/public/.gitkeep +0 -0
- package/templates/postgres/server/context.ts +5 -0
- package/templates/postgres/server/routes/health.ts +5 -0
- package/templates/postgres/server/routes/users.ts +22 -0
- package/templates/postgres/theo.config.ts +3 -0
- package/templates/postgres/tsconfig.json +15 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { resolve as resolve2 } from "path";
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
import { existsSync, cpSync, readFileSync, writeFileSync, renameSync, unlinkSync, readdirSync } from "fs";
|
|
8
|
+
import { resolve, join, dirname } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
function getTemplateDir(templateName = "default") {
|
|
12
|
+
return resolve(__dirname, "../templates", templateName);
|
|
13
|
+
}
|
|
14
|
+
function isValidProjectName(name) {
|
|
15
|
+
return /^[a-z0-9][a-z0-9._-]*$/.test(name);
|
|
16
|
+
}
|
|
17
|
+
function scaffold(targetDir, projectName, templateName = "default") {
|
|
18
|
+
const templateDir = getTemplateDir(templateName);
|
|
19
|
+
if (!existsSync(templateDir)) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Template "${templateName}" not found. Available templates: default, dashboard, api-only, postgres`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
if (!isValidProjectName(projectName)) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Invalid project name "${projectName}". Use lowercase letters, numbers, hyphens, and dots. Must start with a letter or number.`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
if (existsSync(targetDir)) {
|
|
30
|
+
const contents = readdirSync(targetDir);
|
|
31
|
+
if (contents.length > 0) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Directory "${targetDir}" is not empty. Please use an empty directory.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
38
|
+
const gitignoreSrc = join(targetDir, "_gitignore");
|
|
39
|
+
const gitignoreDest = join(targetDir, ".gitignore");
|
|
40
|
+
if (existsSync(gitignoreSrc)) {
|
|
41
|
+
renameSync(gitignoreSrc, gitignoreDest);
|
|
42
|
+
}
|
|
43
|
+
const tmplPath = join(targetDir, "package.json.tmpl");
|
|
44
|
+
if (existsSync(tmplPath)) {
|
|
45
|
+
const content = readFileSync(tmplPath, "utf-8");
|
|
46
|
+
const replaced = content.replace(/\{\{name\}\}/g, projectName);
|
|
47
|
+
writeFileSync(join(targetDir, "package.json"), replaced);
|
|
48
|
+
unlinkSync(tmplPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/pkg-manager.ts
|
|
53
|
+
function detectPkgManager() {
|
|
54
|
+
const ua = process.env.npm_config_user_agent ?? "";
|
|
55
|
+
if (ua.startsWith("yarn")) return "yarn";
|
|
56
|
+
if (ua.startsWith("pnpm")) return "pnpm";
|
|
57
|
+
if (ua.startsWith("bun")) return "bun";
|
|
58
|
+
return "npm";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/install.ts
|
|
62
|
+
import spawn from "cross-spawn";
|
|
63
|
+
function runInstall(cwd, pkgManager) {
|
|
64
|
+
const result = spawn.sync(pkgManager, ["install"], {
|
|
65
|
+
cwd,
|
|
66
|
+
stdio: "inherit",
|
|
67
|
+
env: { ...process.env, NODE_ENV: "development" }
|
|
68
|
+
});
|
|
69
|
+
if (result.status !== 0) {
|
|
70
|
+
throw new Error(`Failed to install dependencies with ${pkgManager}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/cli.ts
|
|
75
|
+
function main() {
|
|
76
|
+
const args = process.argv.slice(2);
|
|
77
|
+
const positionalArgs = args.filter((a) => !a.startsWith("--"));
|
|
78
|
+
const projectName = positionalArgs[0];
|
|
79
|
+
if (!projectName) {
|
|
80
|
+
console.error("Usage: create-theokit <project-name> [--template=name]");
|
|
81
|
+
console.error("");
|
|
82
|
+
console.error("Example:");
|
|
83
|
+
console.error(" npx create-theokit my-app");
|
|
84
|
+
console.error(" npx create-theokit my-app --template=dashboard");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const templateFlag = args.find((a) => a.startsWith("--template="));
|
|
88
|
+
const templateName = templateFlag ? templateFlag.split("=")[1] : "default";
|
|
89
|
+
const targetDir = resolve2(process.cwd(), projectName);
|
|
90
|
+
try {
|
|
91
|
+
console.log(`
|
|
92
|
+
Creating TheoKit project "${projectName}" (template: ${templateName})...
|
|
93
|
+
`);
|
|
94
|
+
scaffold(targetDir, projectName, templateName);
|
|
95
|
+
const pkgManager = detectPkgManager();
|
|
96
|
+
console.log(`Installing dependencies with ${pkgManager}...
|
|
97
|
+
`);
|
|
98
|
+
runInstall(targetDir, pkgManager);
|
|
99
|
+
console.log(`
|
|
100
|
+
\u2713 Project created at ${targetDir}
|
|
101
|
+
`);
|
|
102
|
+
console.log(` Next steps:
|
|
103
|
+
`);
|
|
104
|
+
console.log(` cd ${projectName}`);
|
|
105
|
+
console.log(` ${pkgManager === "npm" ? "npx" : pkgManager} theokit dev
|
|
106
|
+
`);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error(`
|
|
109
|
+
\u2717 ${err.message}
|
|
110
|
+
`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
main();
|
|
115
|
+
export {
|
|
116
|
+
main
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/index.ts","../src/pkg-manager.ts","../src/install.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport { scaffold } from './index.js'\nimport { detectPkgManager } from './pkg-manager.js'\nimport { runInstall } from './install.js'\n\nexport function main(): void {\n const args = process.argv.slice(2)\n const positionalArgs = args.filter((a) => !a.startsWith('--'))\n const projectName = positionalArgs[0]\n\n if (!projectName) {\n console.error('Usage: create-theokit <project-name> [--template=name]')\n console.error('')\n console.error('Example:')\n console.error(' npx create-theokit my-app')\n console.error(' npx create-theokit my-app --template=dashboard')\n process.exit(1)\n }\n\n // Parse --template flag\n const templateFlag = args.find((a) => a.startsWith('--template='))\n const templateName = templateFlag ? templateFlag.split('=')[1] : 'default'\n\n const targetDir = resolve(process.cwd(), projectName)\n\n try {\n console.log(`\\nCreating TheoKit project \"${projectName}\" (template: ${templateName})...\\n`)\n\n scaffold(targetDir, projectName, templateName)\n\n const pkgManager = detectPkgManager()\n console.log(`Installing dependencies with ${pkgManager}...\\n`)\n runInstall(targetDir, pkgManager)\n\n console.log(`\\n ✓ Project created at ${targetDir}\\n`)\n console.log(` Next steps:\\n`)\n console.log(` cd ${projectName}`)\n console.log(` ${pkgManager === 'npm' ? 'npx' : pkgManager} theokit dev\\n`)\n } catch (err) {\n console.error(`\\n ✗ ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// Auto-execute when run as script\nmain()\n","import { existsSync, cpSync, readFileSync, writeFileSync, renameSync, unlinkSync, readdirSync } from 'node:fs'\nimport { resolve, join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nfunction getTemplateDir(templateName = 'default'): string {\n return resolve(__dirname, '../templates', templateName)\n}\n\nfunction isValidProjectName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name)\n}\n\nexport function scaffold(targetDir: string, projectName: string, templateName = 'default'): void {\n const templateDir = getTemplateDir(templateName)\n\n if (!existsSync(templateDir)) {\n throw new Error(\n `Template \"${templateName}\" not found. Available templates: default, dashboard, api-only, postgres`,\n )\n }\n\n if (!isValidProjectName(projectName)) {\n throw new Error(\n `Invalid project name \"${projectName}\". ` +\n `Use lowercase letters, numbers, hyphens, and dots. Must start with a letter or number.`,\n )\n }\n\n if (existsSync(targetDir)) {\n const contents = readdirSync(targetDir)\n if (contents.length > 0) {\n throw new Error(\n `Directory \"${targetDir}\" is not empty. Please use an empty directory.`,\n )\n }\n }\n\n // Copy template\n cpSync(templateDir, targetDir, { recursive: true })\n\n // Rename _gitignore → .gitignore\n const gitignoreSrc = join(targetDir, '_gitignore')\n const gitignoreDest = join(targetDir, '.gitignore')\n if (existsSync(gitignoreSrc)) {\n renameSync(gitignoreSrc, gitignoreDest)\n }\n\n // Process package.json.tmpl → package.json\n const tmplPath = join(targetDir, 'package.json.tmpl')\n if (existsSync(tmplPath)) {\n const content = readFileSync(tmplPath, 'utf-8')\n const replaced = content.replace(/\\{\\{name\\}\\}/g, projectName)\n writeFileSync(join(targetDir, 'package.json'), replaced)\n unlinkSync(tmplPath)\n }\n}\n","export type PkgManager = 'npm' | 'pnpm' | 'yarn' | 'bun'\n\nexport function detectPkgManager(): PkgManager {\n const ua = process.env.npm_config_user_agent ?? ''\n if (ua.startsWith('yarn')) return 'yarn'\n if (ua.startsWith('pnpm')) return 'pnpm'\n if (ua.startsWith('bun')) return 'bun'\n return 'npm'\n}\n","import spawn from 'cross-spawn'\nimport type { PkgManager } from './pkg-manager.js'\n\nexport function runInstall(cwd: string, pkgManager: PkgManager): void {\n const result = spawn.sync(pkgManager, ['install'], {\n cwd,\n stdio: 'inherit',\n env: { ...process.env, NODE_ENV: 'development' },\n })\n\n if (result.status !== 0) {\n throw new Error(`Failed to install dependencies with ${pkgManager}`)\n }\n}\n"],"mappings":"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","names":["resolve","resolve"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-theokit",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Scaffold a new TheoKit project",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsup"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"create-theokit": "./dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"templates"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"cross-spawn": "^7.0.6"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Theo App</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/@theo/entry-client"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "theokit dev"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"theokit": "workspace:*",
|
|
11
|
+
"react": "^19.0.0",
|
|
12
|
+
"react-dom": "^19.0.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "^5.7.0",
|
|
16
|
+
"@types/react": "^19.0.0",
|
|
17
|
+
"@types/react-dom": "^19.0.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineRoute } from 'theokit/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
const users = [
|
|
5
|
+
{ id: '1', name: 'Alice', email: 'alice@example.com' },
|
|
6
|
+
{ id: '2', name: 'Bob', email: 'bob@example.com' },
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
export const GET = defineRoute({
|
|
10
|
+
query: z.object({ search: z.string().optional() }),
|
|
11
|
+
handler: ({ query }) => {
|
|
12
|
+
if (query.search) {
|
|
13
|
+
return users.filter((u) => u.name.toLowerCase().includes(query.search!.toLowerCase()))
|
|
14
|
+
}
|
|
15
|
+
return users
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const POST = defineRoute({
|
|
20
|
+
body: z.object({ name: z.string().min(1), email: z.string().email() }),
|
|
21
|
+
status: 201,
|
|
22
|
+
handler: ({ body }) => ({
|
|
23
|
+
id: String(users.length + 1),
|
|
24
|
+
name: body.name,
|
|
25
|
+
email: body.email,
|
|
26
|
+
}),
|
|
27
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"jsx": "react-jsx",
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["app/**/*.ts", "app/**/*.tsx", "server/**/*.ts"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Outlet } from 'react-router'
|
|
2
|
+
|
|
3
|
+
export default function RootLayout() {
|
|
4
|
+
return (
|
|
5
|
+
<div>
|
|
6
|
+
<nav>
|
|
7
|
+
<a href="/">Home</a> | <a href="/about">About</a> | <a href="/dashboard">Dashboard</a>
|
|
8
|
+
</nav>
|
|
9
|
+
<main>
|
|
10
|
+
<Outlet />
|
|
11
|
+
</main>
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Theo App</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/@theo/entry-client"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "theokit dev"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"theokit": "workspace:*",
|
|
11
|
+
"react": "^19.0.0",
|
|
12
|
+
"react-dom": "^19.0.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "^5.7.0",
|
|
16
|
+
"@types/react": "^19.0.0",
|
|
17
|
+
"@types/react-dom": "^19.0.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"jsx": "react-jsx",
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["app/**/*.ts", "app/**/*.tsx", "server/**/*.ts"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Theo App</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/@theo/entry-client"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "theokit dev"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"theokit": "workspace:*",
|
|
11
|
+
"react": "^19.0.0",
|
|
12
|
+
"react-dom": "^19.0.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "^5.7.0",
|
|
16
|
+
"@types/react": "^19.0.0",
|
|
17
|
+
"@types/react-dom": "^19.0.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"jsx": "react-jsx",
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["app/**/*.ts", "app/**/*.tsx", "server/**/*.ts"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
|
2
|
+
|
|
3
|
+
export const users = pgTable('users', {
|
|
4
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
5
|
+
name: text('name').notNull(),
|
|
6
|
+
email: text('email').notNull().unique(),
|
|
7
|
+
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
8
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Theo App</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/@theo/entry-client"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "theokit dev",
|
|
8
|
+
"build": "theokit build",
|
|
9
|
+
"start": "theokit start",
|
|
10
|
+
"db:push": "drizzle-kit push",
|
|
11
|
+
"db:generate": "drizzle-kit generate",
|
|
12
|
+
"db:migrate": "drizzle-kit migrate",
|
|
13
|
+
"db:studio": "drizzle-kit studio"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"theokit": "workspace:*",
|
|
17
|
+
"react": "^19.0.0",
|
|
18
|
+
"react-dom": "^19.0.0",
|
|
19
|
+
"drizzle-orm": "^0.45.0",
|
|
20
|
+
"postgres": "^3.4.0",
|
|
21
|
+
"zod": "^3.24.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.7.0",
|
|
25
|
+
"@types/react": "^19.0.0",
|
|
26
|
+
"@types/react-dom": "^19.0.0",
|
|
27
|
+
"drizzle-kit": "^0.31.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineRoute } from 'theokit/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { users } from '../../db/schema.js'
|
|
4
|
+
|
|
5
|
+
export const GET = defineRoute({
|
|
6
|
+
handler: async ({ ctx }) => {
|
|
7
|
+
const allUsers = await (ctx as any).db.select().from(users)
|
|
8
|
+
return { users: allUsers }
|
|
9
|
+
},
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export const POST = defineRoute({
|
|
13
|
+
body: z.object({
|
|
14
|
+
name: z.string().min(1),
|
|
15
|
+
email: z.string().email(),
|
|
16
|
+
}),
|
|
17
|
+
status: 201,
|
|
18
|
+
handler: async ({ body, ctx }) => {
|
|
19
|
+
const [user] = await (ctx as any).db.insert(users).values(body).returning()
|
|
20
|
+
return user
|
|
21
|
+
},
|
|
22
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"jsx": "react-jsx",
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["app/**/*.ts", "app/**/*.tsx", "server/**/*.ts"]
|
|
15
|
+
}
|