create-frootkit 1.1.3 → 1.2.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 (109) hide show
  1. package/README.md +50 -16
  2. package/dist/addons/database.d.ts +3 -2
  3. package/dist/addons/database.d.ts.map +1 -1
  4. package/dist/addons/database.js +475 -14
  5. package/dist/addons/database.js.map +1 -1
  6. package/dist/index.js +93 -18
  7. package/dist/index.js.map +1 -1
  8. package/dist/scaffold.d.ts +1 -0
  9. package/dist/scaffold.d.ts.map +1 -1
  10. package/dist/scaffold.js +3 -3
  11. package/dist/scaffold.js.map +1 -1
  12. package/dist/template.d.ts +3 -5
  13. package/dist/template.d.ts.map +1 -1
  14. package/dist/template.js +47 -403
  15. package/dist/template.js.map +1 -1
  16. package/dist/templates/auth.d.ts +3 -0
  17. package/dist/templates/auth.d.ts.map +1 -0
  18. package/dist/templates/auth.js +487 -0
  19. package/dist/templates/auth.js.map +1 -0
  20. package/dist/templates/blog.d.ts +3 -0
  21. package/dist/templates/blog.d.ts.map +1 -0
  22. package/dist/templates/blog.js +369 -0
  23. package/dist/templates/blog.js.map +1 -0
  24. package/dist/templates/common.d.ts +5 -0
  25. package/dist/templates/common.d.ts.map +1 -0
  26. package/dist/templates/common.js +116 -0
  27. package/dist/templates/common.js.map +1 -0
  28. package/dist/templates/ecommerce.d.ts +3 -0
  29. package/dist/templates/ecommerce.d.ts.map +1 -0
  30. package/dist/templates/ecommerce.js +474 -0
  31. package/dist/templates/ecommerce.js.map +1 -0
  32. package/dist/templates/empty.d.ts +3 -0
  33. package/dist/templates/empty.d.ts.map +1 -0
  34. package/dist/templates/empty.js +85 -0
  35. package/dist/templates/empty.js.map +1 -0
  36. package/dist/templates/index.d.ts +16 -0
  37. package/dist/templates/index.d.ts.map +1 -0
  38. package/dist/templates/index.js +32 -0
  39. package/dist/templates/index.js.map +1 -0
  40. package/dist/templates/starter.d.ts +3 -0
  41. package/dist/templates/starter.d.ts.map +1 -0
  42. package/dist/templates/starter.js +308 -0
  43. package/dist/templates/starter.js.map +1 -0
  44. package/dist/templates/types.d.ts +13 -0
  45. package/dist/templates/types.d.ts.map +1 -0
  46. package/dist/templates/types.js +5 -0
  47. package/dist/templates/types.js.map +1 -0
  48. package/package.json +2 -1
  49. package/templates/auth/README.md +11 -0
  50. package/templates/auth/package.json +24 -0
  51. package/templates/auth/src/app/api/auth/route.ts +148 -0
  52. package/templates/auth/src/app/dashboard/layout.tsx +34 -0
  53. package/templates/auth/src/app/dashboard/page.tsx +72 -0
  54. package/templates/auth/src/app/layout.tsx +31 -0
  55. package/templates/auth/src/app/login/page.tsx +18 -0
  56. package/templates/auth/src/app/page.tsx +60 -0
  57. package/templates/auth/src/app/signup/page.tsx +18 -0
  58. package/templates/auth/src/components/AuthForm.tsx +77 -0
  59. package/templates/auth/src/components/DashboardNav.tsx +51 -0
  60. package/templates/auth/src/components/MetricCard.tsx +26 -0
  61. package/templates/auth/src/components/Navbar.tsx +29 -0
  62. package/templates/auth/src/lib/auth.ts +145 -0
  63. package/templates/auth/src/styles.css +19 -0
  64. package/templates/auth/tsconfig.json +13 -0
  65. package/templates/blog/README.md +11 -0
  66. package/templates/blog/package.json +24 -0
  67. package/templates/blog/src/app/api/posts/route.ts +36 -0
  68. package/templates/blog/src/app/blog/[slug]/page.tsx +74 -0
  69. package/templates/blog/src/app/layout.tsx +31 -0
  70. package/templates/blog/src/app/page.tsx +44 -0
  71. package/templates/blog/src/components/Footer.tsx +16 -0
  72. package/templates/blog/src/components/Navbar.tsx +31 -0
  73. package/templates/blog/src/components/Newsletter.tsx +32 -0
  74. package/templates/blog/src/components/PostCard.tsx +47 -0
  75. package/templates/blog/src/data/posts.ts +75 -0
  76. package/templates/blog/src/styles.css +19 -0
  77. package/templates/blog/tsconfig.json +13 -0
  78. package/templates/ecommerce/README.md +11 -0
  79. package/templates/ecommerce/package.json +24 -0
  80. package/templates/ecommerce/src/app/api/checkout/route.ts +19 -0
  81. package/templates/ecommerce/src/app/api/products/route.ts +28 -0
  82. package/templates/ecommerce/src/app/cart/page.tsx +22 -0
  83. package/templates/ecommerce/src/app/layout.tsx +31 -0
  84. package/templates/ecommerce/src/app/page.tsx +59 -0
  85. package/templates/ecommerce/src/app/products/[id]/page.tsx +88 -0
  86. package/templates/ecommerce/src/components/CartDrawer.tsx +64 -0
  87. package/templates/ecommerce/src/components/Footer.tsx +16 -0
  88. package/templates/ecommerce/src/components/Navbar.tsx +32 -0
  89. package/templates/ecommerce/src/components/ProductCard.tsx +52 -0
  90. package/templates/ecommerce/src/data/products.ts +87 -0
  91. package/templates/ecommerce/src/styles.css +19 -0
  92. package/templates/ecommerce/tsconfig.json +13 -0
  93. package/templates/empty/README.md +10 -0
  94. package/templates/empty/package.json +24 -0
  95. package/templates/empty/src/app/layout.tsx +29 -0
  96. package/templates/empty/src/app/page.tsx +38 -0
  97. package/templates/empty/src/styles.css +19 -0
  98. package/templates/empty/tsconfig.json +13 -0
  99. package/templates/starter/README.md +22 -0
  100. package/templates/starter/package.json +24 -0
  101. package/templates/starter/src/app/api/health/route.ts +23 -0
  102. package/templates/starter/src/app/layout.tsx +31 -0
  103. package/templates/starter/src/app/page.tsx +122 -0
  104. package/templates/starter/src/components/FeatureCard.tsx +25 -0
  105. package/templates/starter/src/components/Footer.tsx +16 -0
  106. package/templates/starter/src/components/Header.tsx +45 -0
  107. package/templates/starter/src/components/TerminalPreview.tsx +56 -0
  108. package/templates/starter/src/styles.css +19 -0
  109. package/templates/starter/tsconfig.json +13 -0
package/README.md CHANGED
@@ -1,41 +1,75 @@
1
1
  # create-frootkit
2
2
 
3
- > Official dedicated scaffolder CLI for [FrootKit](https://github.com/orbitnorth/frootkit) — The Zero-Overhead Next.js Alternative.
3
+ > Official dedicated scaffolder CLI for [FrootKit](https://github.com/orbitnorth/frootkit) — The Zero-Overhead Next.js Alternative for Preact.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/create-frootkit.svg)](https://www.npmjs.com/package/create-frootkit)
6
6
  [![license](https://img.shields.io/npm/l/create-frootkit.svg)](https://github.com/orbitnorth/create-frootkit/blob/main/LICENSE)
7
7
 
8
8
  ---
9
9
 
10
- ## ⚡ Usage
10
+ ## ⚡ Quick Start
11
11
 
12
- Scaffold a production-ready FrootKit application in seconds:
12
+ Scaffold a production-ready FrootKit application interactively or via CLI flags:
13
13
 
14
14
  ```bash
15
15
  # Using npm
16
- npm create frootkit@latest my-app
16
+ npm create frootkit@latest
17
17
 
18
- # Using pnpm
19
- pnpm create frootkit my-app
18
+ # Using pnpm (recommended)
19
+ pnpm create frootkit
20
20
 
21
21
  # Using bun
22
- bun create frootkit my-app
22
+ bun create frootkit
23
+
24
+ # Using npx
25
+ npx create-frootkit my-app --template blog --db drizzle
23
26
  ```
24
27
 
25
- ### Options
28
+ ---
29
+
30
+ ## 🎨 Official Templates
31
+
32
+ `create-frootkit` comes with 5 production-ready templates organized in modular directories with polished component architecture and responsive Tailwind CSS v4 styling:
26
33
 
27
- | Flag | Description | Default |
34
+ | Template | Flag | Description |
28
35
  | :--- | :--- | :--- |
29
- | `<directory>` | Target project directory | `.` |
30
- | `--name <name>` | Project package name | Directory name |
31
- | `--pm <manager>` | Package manager (`npm`, `pnpm`, `bun`, `yarn`) | Auto-detected |
32
- | `--git` / `--no-git` | Initialize git repository | `--git` |
33
- | `--install` / `--no-install` | Auto-install dependencies | Prompt |
34
- | `-y`, `--yes` | Skip prompts and use defaults | `false` |
36
+ | **Starter** *(Recommended)* | `--template starter` | Full developer showcase with hero section, feature cards, and `/api/health` route. |
37
+ | **Blog & Content** | `--template blog` | High-performance engineering journal with dynamic `[slug]` pages, post cards, author bios, and `/api/posts` search API. |
38
+ | **E-Commerce Store** | `--template ecommerce` | Digital storefront with product catalog, dynamic `[id]` detail pages, shopping cart view, and `/api/checkout` API. |
39
+ | **SaaS & Authentication** | `--template auth` | Complete SaaS platform with Web Crypto PBKDF2 password hashing, secure session tokens, login/signup forms, and protected `/dashboard` layout. |
40
+ | **Minimal Canvas** | `--template empty` | Clean, blank slate with Preact SSR and Tailwind CSS for custom architectures. |
41
+
42
+ ---
43
+
44
+ ## 🗄️ Database & ORM Integrations
45
+
46
+ Choose your database layer with zero native compilation dependencies (`@libsql/client`):
47
+
48
+ - **Drizzle ORM (`--db drizzle`):** Tailored TypeScript schema (`posts`, `products`, `users`, `sessions`), migrations, Drizzle Studio, and seeding scripts (`pnpm db:seed`).
49
+ - **Prisma (`--db prisma`):** Declarative `prisma/schema.prisma` with matching models, client generation, and seeding.
50
+ - **None (`--db none`):** In-memory mock stores and zero setup required.
51
+
52
+ ---
53
+
54
+ ## ⚙️ CLI Options & Flags
55
+
56
+ | Flag | Shorthand | Description | Default |
57
+ | :--- | :--- | :--- | :--- |
58
+ | `--template <name>` | `-t <name>` | Template (`starter`, `blog`, `ecommerce`, `auth`, `empty`) | `starter` |
59
+ | `--pm <manager>` | | Package manager (`pnpm`, `npm`, `bun`, `yarn`) | Prompt |
60
+ | `--db <orm>` | | Database ORM (`none`, `drizzle`, `prisma`) | `none` |
61
+ | `--linter <tool>` | | Code quality tool (`none`, `biome`, `eslint`) | `none` |
62
+ | `--docker` / `--no-docker` | | Multi-stage Docker container setup | `false` |
63
+ | `--agent` / `--no-agent` | | AI coding guidelines (`AGENT.md` & `.cursorrules`) | `true` |
64
+ | `--git` / `--no-git` | | Initialize Git repository | `true` |
65
+ | `--install` / `--no-install` | | Automatically install dependencies | Prompt |
66
+ | `--schema` | | Output machine-readable JSON schema for AI agents | |
67
+ | `-y`, `--yes` | | Accept all defaults non-interactively | `false` |
68
+ | `-v`, `--version` | | Display version | |
69
+ | `-h`, `--help` | | Display help documentation | |
35
70
 
36
71
  ---
37
72
 
38
73
  ## 📄 License
39
74
 
40
75
  MIT © [OrbitNorth LLC](https://github.com/orbitnorth)
41
-
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Database & ORM Add-on for create-frootkit
3
- * Injects Drizzle ORM or Prisma into generated FrootKit projects with SQLite starters.
3
+ * Injects Drizzle ORM or Prisma into generated FrootKit projects with
4
+ * template-tailored models and seeding (zero C++ dependencies via @libsql/client).
4
5
  */
5
6
  export type DatabaseOption = 'none' | 'drizzle' | 'prisma';
6
7
  export interface PackageJsonLike {
@@ -9,5 +10,5 @@ export interface PackageJsonLike {
9
10
  devDependencies?: Record<string, string>;
10
11
  [key: string]: unknown;
11
12
  }
12
- export declare function applyDatabaseAddon(files: Record<string, string>, dbOption: DatabaseOption): void;
13
+ export declare function applyDatabaseAddon(files: Record<string, string>, dbOption: DatabaseOption, templateId?: string): void;
13
14
  //# sourceMappingURL=database.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/addons/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,QAAQ,EAAE,cAAc,GACvB,IAAI,CAoFN"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/addons/database.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,QAAQ,EAAE,cAAc,EACxB,UAAU,GAAE,MAAkB,GAC7B,IAAI,CAkiBN"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Database & ORM Add-on for create-frootkit
3
- * Injects Drizzle ORM or Prisma into generated FrootKit projects with SQLite starters.
3
+ * Injects Drizzle ORM or Prisma into generated FrootKit projects with
4
+ * template-tailored models and seeding (zero C++ dependencies via @libsql/client).
4
5
  */
5
- export function applyDatabaseAddon(files, dbOption) {
6
+ export function applyDatabaseAddon(files, dbOption, templateId = 'starter') {
6
7
  if (dbOption === 'none')
7
8
  return;
8
9
  const pkgJsonStr = files['package.json'];
@@ -14,11 +15,11 @@ export function applyDatabaseAddon(files, dbOption) {
14
15
  pkg.devDependencies = pkg.devDependencies || {};
15
16
  if (dbOption === 'drizzle') {
16
17
  pkg.dependencies['drizzle-orm'] = '^0.39.0';
17
- pkg.dependencies['better-sqlite3'] = '^11.8.0';
18
- pkg.devDependencies['@types/better-sqlite3'] = '^7.6.12';
18
+ pkg.dependencies['@libsql/client'] = '^0.14.0';
19
19
  pkg.devDependencies['drizzle-kit'] = '^0.30.0';
20
20
  pkg.scripts['db:push'] = 'drizzle-kit push';
21
21
  pkg.scripts['db:studio'] = 'drizzle-kit studio';
22
+ pkg.scripts['db:seed'] = 'tsx src/db/seed.ts';
22
23
  files['drizzle.config.ts'] = `import { defineConfig } from 'drizzle-kit';
23
24
 
24
25
  export default defineConfig({
@@ -26,29 +27,271 @@ export default defineConfig({
26
27
  out: './drizzle',
27
28
  dialect: 'sqlite',
28
29
  dbCredentials: {
29
- url: 'sqlite.db',
30
+ url: 'file:sqlite.db',
30
31
  },
31
32
  });
32
33
  `;
33
- files['src/db/schema.ts'] = `import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
34
+ files['src/db/index.ts'] = `import { createClient } from '@libsql/client';
35
+ import { drizzle } from 'drizzle-orm/libsql';
36
+ import * as schema from './schema.js';
37
+
38
+ const client = createClient({
39
+ url: process.env.DATABASE_URL || 'file:sqlite.db',
40
+ });
41
+
42
+ export const db = drizzle(client, { schema });
43
+ `;
44
+ if (templateId === 'blog') {
45
+ files['src/db/schema.ts'] = `import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
46
+
47
+ export const authors = sqliteTable('authors', {
48
+ id: text('id').primaryKey(),
49
+ name: text('name').notNull(),
50
+ role: text('role').notNull(),
51
+ avatar: text('avatar').notNull(),
52
+ bio: text('bio'),
53
+ createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
54
+ });
55
+
56
+ export const posts = sqliteTable('posts', {
57
+ id: text('id').primaryKey(),
58
+ slug: text('slug').notNull().unique(),
59
+ title: text('title').notNull(),
60
+ summary: text('summary').notNull(),
61
+ content: text('content').notNull(), // JSON string or markdown
62
+ category: text('category').notNull().default('General'),
63
+ authorId: text('author_id').references(() => authors.id),
64
+ readTime: text('read_time').notNull().default('3 min read'),
65
+ publishedAt: text('published_at').notNull().$defaultFn(() => new Date().toISOString()),
66
+ });
67
+
68
+ export const tags = sqliteTable('tags', {
69
+ id: text('id').primaryKey(),
70
+ name: text('name').notNull().unique(),
71
+ slug: text('slug').notNull().unique(),
72
+ });
73
+
74
+ export type Post = typeof posts.$inferSelect;
75
+ export type Author = typeof authors.$inferSelect;
76
+ `;
77
+ files['src/db/seed.ts'] = `import { db } from './index.js';
78
+ import { authors, posts, tags } from './schema.js';
79
+
80
+ async function seed() {
81
+ console.log('Seeding blog database...');
82
+
83
+ const authorId = 'auth_alex';
84
+ await db.insert(authors).values({
85
+ id: authorId,
86
+ name: 'Alex Rivera',
87
+ role: 'Staff Systems Architect',
88
+ avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop',
89
+ bio: 'Pioneering sub-millisecond server rendering and edge runtimes.',
90
+ }).onConflictDoNothing();
91
+
92
+ await db.insert(posts).values([
93
+ {
94
+ id: 'post_1',
95
+ slug: 'welcome-to-frootkit',
96
+ title: 'Introducing FrootKit: High-Performance Next.js Alternative',
97
+ summary: 'Why we combined Preact SSR, native Tailwind CSS v4, and the Froot runtime for maximum velocity.',
98
+ content: JSON.stringify([
99
+ 'Modern web frameworks have grown increasingly heavy.',
100
+ 'FrootKit provides sub-millisecond server rendering with zero configuration.',
101
+ ]),
102
+ category: 'Architecture',
103
+ authorId,
104
+ readTime: '4 min read',
105
+ },
106
+ {
107
+ id: 'post_2',
108
+ slug: 'tailwind-v4-without-configs',
109
+ title: 'Zero Configuration Tailwind CSS v4 in Practice',
110
+ summary: 'Explore how native CSS @theme variables simplify fullstack design tokens.',
111
+ content: JSON.stringify([
112
+ 'Tailwind CSS v4 redefines utility styling directly inside CSS without js config files.',
113
+ ]),
114
+ category: 'Styling',
115
+ authorId,
116
+ readTime: '3 min read',
117
+ },
118
+ ]).onConflictDoNothing();
119
+
120
+ console.log('✓ Seeding complete!');
121
+ }
122
+
123
+ seed().catch(console.error);
124
+ `;
125
+ }
126
+ else if (templateId === 'ecommerce') {
127
+ files['src/db/schema.ts'] = `import { sqliteTable, text, real, integer } from 'drizzle-orm/sqlite-core';
128
+
129
+ export const products = sqliteTable('products', {
130
+ id: text('id').primaryKey(),
131
+ name: text('name').notNull(),
132
+ price: real('price').notNull(),
133
+ category: text('category').notNull(),
134
+ rating: real('rating').notNull().default(5.0),
135
+ reviewsCount: integer('reviews_count').notNull().default(0),
136
+ image: text('image').notNull(),
137
+ description: text('description').notNull(),
138
+ features: text('features').notNull(), // JSON string array
139
+ inStock: integer('in_stock', { mode: 'boolean' }).notNull().default(true),
140
+ createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
141
+ });
142
+
143
+ export const orders = sqliteTable('orders', {
144
+ id: text('id').primaryKey(),
145
+ customerEmail: text('customer_email').notNull(),
146
+ total: real('total').notNull(),
147
+ status: text('status').notNull().default('pending'),
148
+ itemsJson: text('items_json').notNull(),
149
+ createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
150
+ });
151
+
152
+ export const cartItems = sqliteTable('cart_items', {
153
+ id: text('id').primaryKey(),
154
+ sessionId: text('session_id').notNull(),
155
+ productId: text('product_id').notNull().references(() => products.id),
156
+ quantity: integer('quantity').notNull().default(1),
157
+ });
158
+
159
+ export type Product = typeof products.$inferSelect;
160
+ export type Order = typeof orders.$inferSelect;
161
+ `;
162
+ files['src/db/seed.ts'] = `import { db } from './index.js';
163
+ import { products } from './schema.js';
164
+
165
+ async function seed() {
166
+ console.log('Seeding e-commerce store database...');
167
+
168
+ await db.insert(products).values([
169
+ {
170
+ id: 'nordic-mechanical-keyboard',
171
+ name: 'Nordic Minimal Mechanical Keyboard',
172
+ price: 149.0,
173
+ category: 'Keyboards',
174
+ rating: 4.9,
175
+ reviewsCount: 128,
176
+ image: 'https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=600&h=450&fit=crop',
177
+ description: 'Aircraft-grade anodized aluminum body with custom hot-swappable linear switches.',
178
+ features: JSON.stringify(['Hot-swappable switches', 'Bluetooth 5.3', 'CNC Aluminum Chassis']),
179
+ inStock: true,
180
+ },
181
+ {
182
+ id: 'studio-monitor-headphones',
183
+ name: 'Studio Master Pro Headphones',
184
+ price: 289.0,
185
+ category: 'Audio',
186
+ rating: 4.8,
187
+ reviewsCount: 94,
188
+ image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600&h=450&fit=crop',
189
+ description: 'Precision 50mm dynamic drivers engineered for ultra-flat frequency response.',
190
+ features: JSON.stringify(['Planar Magnetic Sound', 'Memory Foam Cushions', 'Detachable Silver Cable']),
191
+ inStock: true,
192
+ },
193
+ ]).onConflictDoNothing();
194
+
195
+ console.log('✓ Seeding complete!');
196
+ }
197
+
198
+ seed().catch(console.error);
199
+ `;
200
+ }
201
+ else if (templateId === 'auth') {
202
+ files['src/db/schema.ts'] = `import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
203
+
204
+ export const users = sqliteTable('users', {
205
+ id: text('id').primaryKey(),
206
+ email: text('email').notNull().unique(),
207
+ passwordHash: text('password_hash').notNull(),
208
+ name: text('name').notNull(),
209
+ role: text('role').notNull().default('user'),
210
+ createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
211
+ });
212
+
213
+ export const sessions = sqliteTable('sessions', {
214
+ id: text('id').primaryKey(),
215
+ token: text('token').notNull().unique(),
216
+ userId: text('user_id').notNull().references(() => users.id),
217
+ expiresAt: text('expires_at').notNull(),
218
+ createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
219
+ });
220
+
221
+ export const workspaces = sqliteTable('workspaces', {
222
+ id: text('id').primaryKey(),
223
+ name: text('name').notNull(),
224
+ ownerId: text('owner_id').notNull().references(() => users.id),
225
+ plan: text('plan').notNull().default('starter'),
226
+ createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
227
+ });
228
+
229
+ export type User = typeof users.$inferSelect;
230
+ export type Session = typeof sessions.$inferSelect;
231
+ `;
232
+ files['src/db/seed.ts'] = `import { db } from './index.js';
233
+ import { users, workspaces } from './schema.js';
234
+ import { hashPassword } from '../lib/auth.js';
235
+
236
+ async function seed() {
237
+ console.log('Seeding SaaS database with demo user...');
238
+
239
+ const passwordHash = await hashPassword('password123');
240
+ const adminId = 'usr_admin_demo';
241
+
242
+ await db.insert(users).values({
243
+ id: adminId,
244
+ email: 'admin@frootkit.dev',
245
+ passwordHash,
246
+ name: 'Sarah Connor',
247
+ role: 'admin',
248
+ }).onConflictDoNothing();
249
+
250
+ await db.insert(workspaces).values({
251
+ id: 'ws_demo_01',
252
+ name: "Sarah's Production Workspace",
253
+ ownerId: adminId,
254
+ plan: 'enterprise',
255
+ }).onConflictDoNothing();
256
+
257
+ console.log('✓ Seeding complete! Demo account created: admin@frootkit.dev / password123');
258
+ }
259
+
260
+ seed().catch(console.error);
261
+ `;
262
+ }
263
+ else {
264
+ // Starter / default
265
+ files['src/db/schema.ts'] = `import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
34
266
 
35
267
  export const users = sqliteTable('users', {
36
268
  id: text('id').primaryKey(),
37
269
  name: text('name').notNull(),
38
270
  email: text('email').notNull().unique(),
271
+ role: text('role').notNull().default('user'),
39
272
  createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
40
273
  });
41
274
 
42
275
  export type User = typeof users.$inferSelect;
43
276
  export type NewUser = typeof users.$inferInsert;
44
277
  `;
45
- files['src/db/index.ts'] = `import Database from 'better-sqlite3';
46
- import { drizzle } from 'drizzle-orm/better-sqlite3';
47
- import * as schema from './schema.js';
278
+ files['src/db/seed.ts'] = `import { db } from './index.js';
279
+ import { users } from './schema.js';
280
+
281
+ async function seed() {
282
+ console.log('Seeding database...');
283
+ await db.insert(users).values({
284
+ id: 'usr_starter_1',
285
+ name: 'Developer',
286
+ email: 'developer@frootkit.dev',
287
+ role: 'admin',
288
+ }).onConflictDoNothing();
289
+ console.log('✓ Seeding complete!');
290
+ }
48
291
 
49
- const sqlite = new Database('sqlite.db');
50
- export const db = drizzle(sqlite, { schema });
292
+ seed().catch(console.error);
51
293
  `;
294
+ }
52
295
  }
53
296
  else if (dbOption === 'prisma') {
54
297
  pkg.dependencies['@prisma/client'] = '^6.4.0';
@@ -56,7 +299,209 @@ export const db = drizzle(sqlite, { schema });
56
299
  pkg.scripts['db:generate'] = 'prisma generate';
57
300
  pkg.scripts['db:push'] = 'prisma db push';
58
301
  pkg.scripts['db:studio'] = 'prisma studio';
59
- files['prisma/schema.prisma'] = `datasource db {
302
+ pkg.scripts['db:seed'] = 'tsx src/db/seed.ts';
303
+ files['src/db/index.ts'] = `import { PrismaClient } from '@prisma/client';
304
+
305
+ export const prisma = new PrismaClient();
306
+ `;
307
+ if (templateId === 'blog') {
308
+ files['prisma/schema.prisma'] = `datasource db {
309
+ provider = "sqlite"
310
+ url = "file:./dev.db"
311
+ }
312
+
313
+ generator client {
314
+ provider = "prisma-client-js"
315
+ }
316
+
317
+ model Author {
318
+ id String @id @default(uuid())
319
+ name String
320
+ role String
321
+ avatar String
322
+ bio String?
323
+ posts Post[]
324
+ createdAt DateTime @default(now())
325
+ }
326
+
327
+ model Post {
328
+ id String @id @default(uuid())
329
+ slug String @unique
330
+ title String
331
+ summary String
332
+ content String
333
+ category String @default("General")
334
+ authorId String?
335
+ author Author? @relation(fields: [authorId], references: [id])
336
+ readTime String @default("3 min read")
337
+ publishedAt DateTime @default(now())
338
+ }
339
+
340
+ model Tag {
341
+ id String @id @default(uuid())
342
+ name String @unique
343
+ slug String @unique
344
+ }
345
+ `;
346
+ files['src/db/seed.ts'] = `import { prisma } from './index.js';
347
+
348
+ async function seed() {
349
+ console.log('Seeding blog database with Prisma...');
350
+ const author = await prisma.author.upsert({
351
+ where: { id: 'auth_alex' },
352
+ update: {},
353
+ create: {
354
+ id: 'auth_alex',
355
+ name: 'Alex Rivera',
356
+ role: 'Staff Systems Architect',
357
+ avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop',
358
+ bio: 'Pioneering sub-millisecond server rendering.',
359
+ },
360
+ });
361
+
362
+ await prisma.post.upsert({
363
+ where: { slug: 'welcome-to-frootkit' },
364
+ update: {},
365
+ create: {
366
+ slug: 'welcome-to-frootkit',
367
+ title: 'Introducing FrootKit: High-Performance Next.js Alternative',
368
+ summary: 'Why we combined Preact SSR and Tailwind CSS v4.',
369
+ content: 'FrootKit provides sub-millisecond server rendering.',
370
+ authorId: author.id,
371
+ category: 'Architecture',
372
+ },
373
+ });
374
+
375
+ console.log('✓ Seeding complete!');
376
+ }
377
+
378
+ seed().catch(console.error);
379
+ `;
380
+ }
381
+ else if (templateId === 'ecommerce') {
382
+ files['prisma/schema.prisma'] = `datasource db {
383
+ provider = "sqlite"
384
+ url = "file:./dev.db"
385
+ }
386
+
387
+ generator client {
388
+ provider = "prisma-client-js"
389
+ }
390
+
391
+ model Product {
392
+ id String @id
393
+ name String
394
+ price Float
395
+ category String
396
+ rating Float @default(5.0)
397
+ reviewsCount Int @default(0)
398
+ image String
399
+ description String
400
+ features String
401
+ inStock Boolean @default(true)
402
+ createdAt DateTime @default(now())
403
+ }
404
+
405
+ model Order {
406
+ id String @id @default(uuid())
407
+ customerEmail String
408
+ total Float
409
+ status String @default("pending")
410
+ itemsJson String
411
+ createdAt DateTime @default(now())
412
+ }
413
+ `;
414
+ files['src/db/seed.ts'] = `import { prisma } from './index.js';
415
+
416
+ async function seed() {
417
+ console.log('Seeding e-commerce store with Prisma...');
418
+ await prisma.product.upsert({
419
+ where: { id: 'nordic-mechanical-keyboard' },
420
+ update: {},
421
+ create: {
422
+ id: 'nordic-mechanical-keyboard',
423
+ name: 'Nordic Minimal Mechanical Keyboard',
424
+ price: 149.0,
425
+ category: 'Keyboards',
426
+ rating: 4.9,
427
+ reviewsCount: 128,
428
+ image: 'https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=600&h=450&fit=crop',
429
+ description: 'Aircraft-grade anodized aluminum body with custom hot-swappable linear switches.',
430
+ features: '["Hot-swappable switches", "Bluetooth 5.3", "CNC Aluminum Chassis"]',
431
+ inStock: true,
432
+ },
433
+ });
434
+ console.log('✓ Seeding complete!');
435
+ }
436
+
437
+ seed().catch(console.error);
438
+ `;
439
+ }
440
+ else if (templateId === 'auth') {
441
+ files['prisma/schema.prisma'] = `datasource db {
442
+ provider = "sqlite"
443
+ url = "file:./dev.db"
444
+ }
445
+
446
+ generator client {
447
+ provider = "prisma-client-js"
448
+ }
449
+
450
+ model User {
451
+ id String @id @default(uuid())
452
+ email String @unique
453
+ passwordHash String
454
+ name String
455
+ role String @default("user")
456
+ sessions Session[]
457
+ workspaces Workspace[]
458
+ createdAt DateTime @default(now())
459
+ }
460
+
461
+ model Session {
462
+ id String @id @default(uuid())
463
+ token String @unique
464
+ userId String
465
+ user User @relation(fields: [userId], references: [id])
466
+ expiresAt DateTime
467
+ createdAt DateTime @default(now())
468
+ }
469
+
470
+ model Workspace {
471
+ id String @id @default(uuid())
472
+ name String
473
+ ownerId String
474
+ owner User @relation(fields: [ownerId], references: [id])
475
+ plan String @default("starter")
476
+ createdAt DateTime @default(now())
477
+ }
478
+ `;
479
+ files['src/db/seed.ts'] = `import { prisma } from './index.js';
480
+ import { hashPassword } from '../lib/auth.js';
481
+
482
+ async function seed() {
483
+ console.log('Seeding auth database with Prisma...');
484
+ const passwordHash = await hashPassword('password123');
485
+
486
+ await prisma.user.upsert({
487
+ where: { email: 'admin@frootkit.dev' },
488
+ update: {},
489
+ create: {
490
+ email: 'admin@frootkit.dev',
491
+ passwordHash,
492
+ name: 'Sarah Connor',
493
+ role: 'admin',
494
+ },
495
+ });
496
+
497
+ console.log('✓ Seeding complete! Demo credentials: admin@frootkit.dev / password123');
498
+ }
499
+
500
+ seed().catch(console.error);
501
+ `;
502
+ }
503
+ else {
504
+ files['prisma/schema.prisma'] = `datasource db {
60
505
  provider = "sqlite"
61
506
  url = "file:./dev.db"
62
507
  }
@@ -69,13 +514,29 @@ model User {
69
514
  id String @id @default(uuid())
70
515
  email String @unique
71
516
  name String
517
+ role String @default("user")
72
518
  createdAt DateTime @default(now())
73
519
  }
74
520
  `;
75
- files['src/db/index.ts'] = `import { PrismaClient } from '@prisma/client';
521
+ files['src/db/seed.ts'] = `import { prisma } from './index.js';
76
522
 
77
- export const prisma = new PrismaClient();
523
+ async function seed() {
524
+ console.log('Seeding starter database with Prisma...');
525
+ await prisma.user.upsert({
526
+ where: { email: 'developer@frootkit.dev' },
527
+ update: {},
528
+ create: {
529
+ email: 'developer@frootkit.dev',
530
+ name: 'Developer',
531
+ role: 'admin',
532
+ },
533
+ });
534
+ console.log('✓ Seeding complete!');
535
+ }
536
+
537
+ seed().catch(console.error);
78
538
  `;
539
+ }
79
540
  }
80
541
  files['package.json'] = JSON.stringify(pkg, null, 2) + '\n';
81
542
  }
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/addons/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,UAAU,kBAAkB,CAChC,KAA6B,EAC7B,QAAwB;IAExB,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO;IAEhC,MAAM,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;IACzC,IAAI,CAAC,UAAU;QAAE,OAAO;IAExB,MAAM,GAAG,GAAoB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACpD,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;IAChC,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;IAC1C,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;IAEhD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,GAAG,CAAC,YAAY,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC;QAC5C,GAAG,CAAC,YAAY,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC;QAC/C,GAAG,CAAC,eAAe,CAAC,uBAAuB,CAAC,GAAG,SAAS,CAAC;QACzD,GAAG,CAAC,eAAe,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC;QAE/C,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,kBAAkB,CAAC;QAC5C,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,oBAAoB,CAAC;QAEhD,KAAK,CAAC,mBAAmB,CAAC,GAAG;;;;;;;;;;CAUhC,CAAC;QAEE,KAAK,CAAC,kBAAkB,CAAC,GAAG;;;;;;;;;;;CAW/B,CAAC;QAEE,KAAK,CAAC,iBAAiB,CAAC,GAAG;;;;;;CAM9B,CAAC;IACA,CAAC;SAAM,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,GAAG,CAAC,YAAY,CAAC,gBAAgB,CAAC,GAAG,QAAQ,CAAC;QAC9C,GAAG,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;QAEzC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,iBAAiB,CAAC;QAC/C,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,gBAAgB,CAAC;QAC1C,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,eAAe,CAAC;QAE3C,KAAK,CAAC,sBAAsB,CAAC,GAAG;;;;;;;;;;;;;;;CAenC,CAAC;QAEE,KAAK,CAAC,iBAAiB,CAAC,GAAG;;;CAG9B,CAAC;IACA,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/addons/database.ts"],"names":[],"mappings":"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"}