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.
Files changed (48) hide show
  1. package/dist/cli.js +118 -0
  2. package/dist/cli.js.map +1 -0
  3. package/package.json +19 -0
  4. package/templates/api-only/_gitignore +5 -0
  5. package/templates/api-only/app/page.tsx +3 -0
  6. package/templates/api-only/index.html +12 -0
  7. package/templates/api-only/package.json.tmpl +19 -0
  8. package/templates/api-only/public/.gitkeep +0 -0
  9. package/templates/api-only/server/routes/health.ts +5 -0
  10. package/templates/api-only/server/routes/users.ts +27 -0
  11. package/templates/api-only/theo.config.ts +3 -0
  12. package/templates/api-only/tsconfig.json +15 -0
  13. package/templates/dashboard/_gitignore +5 -0
  14. package/templates/dashboard/app/about/page.tsx +3 -0
  15. package/templates/dashboard/app/dashboard/layout.tsx +10 -0
  16. package/templates/dashboard/app/dashboard/page.tsx +3 -0
  17. package/templates/dashboard/app/layout.tsx +14 -0
  18. package/templates/dashboard/app/page.tsx +8 -0
  19. package/templates/dashboard/index.html +12 -0
  20. package/templates/dashboard/package.json.tmpl +19 -0
  21. package/templates/dashboard/public/.gitkeep +0 -0
  22. package/templates/dashboard/server/routes/health.ts +5 -0
  23. package/templates/dashboard/theo.config.ts +3 -0
  24. package/templates/dashboard/tsconfig.json +15 -0
  25. package/templates/default/_gitignore +5 -0
  26. package/templates/default/app/layout.tsx +3 -0
  27. package/templates/default/app/page.tsx +3 -0
  28. package/templates/default/index.html +12 -0
  29. package/templates/default/package.json.tmpl +19 -0
  30. package/templates/default/public/.gitkeep +0 -0
  31. package/templates/default/server/routes/health.ts +5 -0
  32. package/templates/default/theo.config.ts +3 -0
  33. package/templates/default/tsconfig.json +15 -0
  34. package/templates/postgres/.env.example +5 -0
  35. package/templates/postgres/_gitignore +5 -0
  36. package/templates/postgres/app/layout.tsx +14 -0
  37. package/templates/postgres/app/page.tsx +8 -0
  38. package/templates/postgres/db/index.ts +7 -0
  39. package/templates/postgres/db/schema.ts +8 -0
  40. package/templates/postgres/drizzle.config.ts +10 -0
  41. package/templates/postgres/index.html +12 -0
  42. package/templates/postgres/package.json.tmpl +29 -0
  43. package/templates/postgres/public/.gitkeep +0 -0
  44. package/templates/postgres/server/context.ts +5 -0
  45. package/templates/postgres/server/routes/health.ts +5 -0
  46. package/templates/postgres/server/routes/users.ts +22 -0
  47. package/templates/postgres/theo.config.ts +3 -0
  48. 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
@@ -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,5 @@
1
+ node_modules
2
+ .theo
3
+ dist
4
+ .env
5
+ .env.local
@@ -0,0 +1,3 @@
1
+ export default function Page() {
2
+ return <h1>Theo API Server</h1>
3
+ }
@@ -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,5 @@
1
+ import { defineRoute } from 'theokit/server'
2
+
3
+ export const GET = defineRoute({
4
+ handler: () => ({ ok: true }),
5
+ })
@@ -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,3 @@
1
+ import { defineConfig } from 'theokit'
2
+
3
+ export default defineConfig({})
@@ -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,5 @@
1
+ node_modules
2
+ .theo
3
+ dist
4
+ .env
5
+ .env.local
@@ -0,0 +1,3 @@
1
+ export default function AboutPage() {
2
+ return <h1>About</h1>
3
+ }
@@ -0,0 +1,10 @@
1
+ import { Outlet } from 'react-router'
2
+
3
+ export default function DashboardLayout() {
4
+ return (
5
+ <div>
6
+ <h2>Dashboard Area</h2>
7
+ <Outlet />
8
+ </div>
9
+ )
10
+ }
@@ -0,0 +1,3 @@
1
+ export default function DashboardPage() {
2
+ return <h1>Dashboard</h1>
3
+ }
@@ -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,8 @@
1
+ export default function HomePage() {
2
+ return (
3
+ <div>
4
+ <h1>Welcome to Theo</h1>
5
+ <p>Your fullstack TypeScript framework.</p>
6
+ </div>
7
+ )
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,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,5 @@
1
+ import { defineRoute } from 'theokit/server'
2
+
3
+ export const GET = defineRoute({
4
+ handler: () => ({ ok: true }),
5
+ })
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from 'theokit'
2
+
3
+ export default defineConfig({})
@@ -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,5 @@
1
+ node_modules
2
+ .theo
3
+ dist
4
+ .env
5
+ .env.local
@@ -0,0 +1,3 @@
1
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
2
+ return children
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function Page() {
2
+ return <h1>Hello Theo</h1>
3
+ }
@@ -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,5 @@
1
+ import { defineRoute } from 'theokit/server'
2
+
3
+ export const GET = defineRoute({
4
+ handler: () => ({ ok: true }),
5
+ })
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from 'theokit'
2
+
3
+ export default defineConfig({})
@@ -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,5 @@
1
+ # Database connection string
2
+ DATABASE_URL=postgresql://user:password@localhost:5432/mydb
3
+
4
+ # Session secret (min 32 characters)
5
+ # SESSION_SECRET=change-me-to-a-random-string-at-least-32-chars
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ .theo
3
+ dist
4
+ .env
5
+ .env.local
@@ -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>
8
+ </nav>
9
+ <main>
10
+ <Outlet />
11
+ </main>
12
+ </div>
13
+ )
14
+ }
@@ -0,0 +1,8 @@
1
+ export default function Page() {
2
+ return (
3
+ <div>
4
+ <h1>Theo + Postgres</h1>
5
+ <p>Full-stack TypeScript with Drizzle ORM</p>
6
+ </div>
7
+ )
8
+ }
@@ -0,0 +1,7 @@
1
+ import { drizzle } from 'drizzle-orm/postgres-js'
2
+ import postgres from 'postgres'
3
+ import * as schema from './schema.js'
4
+
5
+ const client = postgres(process.env.DATABASE_URL!)
6
+
7
+ export const db = drizzle(client, { schema })
@@ -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,10 @@
1
+ import { defineConfig } from 'drizzle-kit'
2
+
3
+ export default defineConfig({
4
+ out: './drizzle',
5
+ dialect: 'postgresql',
6
+ schema: './db/schema.ts',
7
+ dbCredentials: {
8
+ url: process.env.DATABASE_URL!,
9
+ },
10
+ })
@@ -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,5 @@
1
+ import { db } from '../db/index.js'
2
+
3
+ export function createContext() {
4
+ return { db }
5
+ }
@@ -0,0 +1,5 @@
1
+ import { defineRoute } from 'theokit/server'
2
+
3
+ export const GET = defineRoute({
4
+ handler: () => ({ ok: true }),
5
+ })
@@ -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,3 @@
1
+ import { defineConfig } from 'theokit'
2
+
3
+ export default defineConfig({})
@@ -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
+ }