create-frootkit 1.2.9 → 1.2.11

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.
@@ -1,8 +1,226 @@
1
1
  /**
2
2
  * Database & ORM Add-on for create-frootkit
3
3
  * Injects Drizzle ORM or Prisma into generated FrootKit projects with
4
- * template-tailored models and seeding (zero C++ dependencies via @libsql/client).
4
+ * template-tailored models and real production seeding (zero C++ dependencies via @libsql/client).
5
5
  */
6
+ import * as path from 'node:path';
7
+ import * as fs from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ const require = createRequire(import.meta.url);
10
+ export const ADMIN_PASSWORD_HASH = 'pbkdf2:sha256:100000:cbbb127ce3a186856aed9805e890e625:284c9b9655dc6cdd14da488ee33dd9296e188514c59e960bb6c7137e9a6f468c';
11
+ /**
12
+ * Returns raw SQL DDL for initial table creation
13
+ */
14
+ export function getTemplateDdl(templateId) {
15
+ if (templateId === 'ecommerce') {
16
+ return `
17
+ CREATE TABLE IF NOT EXISTS products (
18
+ id TEXT PRIMARY KEY,
19
+ name TEXT NOT NULL,
20
+ price REAL NOT NULL,
21
+ category TEXT NOT NULL,
22
+ rating REAL NOT NULL DEFAULT 5.0,
23
+ reviews_count INTEGER NOT NULL DEFAULT 0,
24
+ image TEXT NOT NULL,
25
+ description TEXT NOT NULL,
26
+ features TEXT NOT NULL,
27
+ in_stock INTEGER NOT NULL DEFAULT 1,
28
+ created_at TEXT NOT NULL
29
+ );
30
+
31
+ CREATE TABLE IF NOT EXISTS orders (
32
+ id TEXT PRIMARY KEY,
33
+ customer_email TEXT NOT NULL,
34
+ total REAL NOT NULL,
35
+ status TEXT NOT NULL DEFAULT 'pending',
36
+ items_json TEXT NOT NULL,
37
+ created_at TEXT NOT NULL
38
+ );
39
+
40
+ CREATE TABLE IF NOT EXISTS cart_items (
41
+ id TEXT PRIMARY KEY,
42
+ session_id TEXT NOT NULL,
43
+ product_id TEXT NOT NULL REFERENCES products(id),
44
+ quantity INTEGER NOT NULL DEFAULT 1
45
+ );
46
+ `;
47
+ }
48
+ if (templateId === 'blog') {
49
+ return `
50
+ CREATE TABLE IF NOT EXISTS authors (
51
+ id TEXT PRIMARY KEY,
52
+ name TEXT NOT NULL,
53
+ role TEXT NOT NULL,
54
+ avatar TEXT NOT NULL,
55
+ bio TEXT,
56
+ created_at TEXT NOT NULL
57
+ );
58
+
59
+ CREATE TABLE IF NOT EXISTS posts (
60
+ id TEXT PRIMARY KEY,
61
+ slug TEXT NOT NULL UNIQUE,
62
+ title TEXT NOT NULL,
63
+ summary TEXT NOT NULL,
64
+ content TEXT NOT NULL,
65
+ category TEXT NOT NULL DEFAULT 'General',
66
+ author_id TEXT REFERENCES authors(id),
67
+ read_time TEXT NOT NULL DEFAULT '3 min read',
68
+ published_at TEXT NOT NULL
69
+ );
70
+
71
+ CREATE TABLE IF NOT EXISTS tags (
72
+ id TEXT PRIMARY KEY,
73
+ name TEXT NOT NULL UNIQUE,
74
+ slug TEXT NOT NULL UNIQUE
75
+ );
76
+ `;
77
+ }
78
+ if (templateId === 'auth') {
79
+ return `
80
+ CREATE TABLE IF NOT EXISTS users (
81
+ id TEXT PRIMARY KEY,
82
+ email TEXT NOT NULL UNIQUE,
83
+ password_hash TEXT NOT NULL,
84
+ name TEXT NOT NULL,
85
+ role TEXT NOT NULL DEFAULT 'user',
86
+ created_at TEXT NOT NULL
87
+ );
88
+
89
+ CREATE TABLE IF NOT EXISTS sessions (
90
+ id TEXT PRIMARY KEY,
91
+ token TEXT NOT NULL UNIQUE,
92
+ user_id TEXT NOT NULL REFERENCES users(id),
93
+ expires_at TEXT NOT NULL,
94
+ created_at TEXT NOT NULL
95
+ );
96
+
97
+ CREATE TABLE IF NOT EXISTS workspaces (
98
+ id TEXT PRIMARY KEY,
99
+ name TEXT NOT NULL,
100
+ owner_id TEXT NOT NULL REFERENCES users(id),
101
+ plan TEXT NOT NULL DEFAULT 'starter',
102
+ created_at TEXT NOT NULL
103
+ );
104
+
105
+ CREATE TABLE IF NOT EXISTS activity_logs (
106
+ id TEXT PRIMARY KEY,
107
+ user_id TEXT NOT NULL REFERENCES users(id),
108
+ action TEXT NOT NULL,
109
+ ip_address TEXT,
110
+ created_at TEXT NOT NULL
111
+ );
112
+ `;
113
+ }
114
+ // starter / empty
115
+ return `
116
+ CREATE TABLE IF NOT EXISTS metrics (
117
+ id TEXT PRIMARY KEY,
118
+ name TEXT NOT NULL,
119
+ value TEXT NOT NULL,
120
+ unit TEXT NOT NULL,
121
+ recorded_at TEXT NOT NULL
122
+ );
123
+
124
+ CREATE TABLE IF NOT EXISTS system_health (
125
+ id TEXT PRIMARY KEY,
126
+ status TEXT NOT NULL,
127
+ uptime REAL NOT NULL,
128
+ latency_ms REAL NOT NULL,
129
+ checked_at TEXT NOT NULL
130
+ );
131
+ `;
132
+ }
133
+ /**
134
+ * Returns raw SQL statements for seeding actual data (not dummy!)
135
+ */
136
+ export function getTemplateSeedSql(templateId) {
137
+ if (templateId === 'ecommerce') {
138
+ return `
139
+ INSERT OR IGNORE INTO products (id, name, price, category, rating, reviews_count, image, description, features, in_stock, created_at) VALUES
140
+ ('nordic-mechanical-keyboard', 'Nordic Minimal Mechanical Keyboard', 149.0, 'Keyboards', 4.9, 128, 'https://images.unsplash.com/photo-1595225476474-87563907a212?w=800&auto=format&fit=crop&q=80', 'Aircraft-grade CNC anodized aluminum chassis with custom factory-lubed linear switches and sound-dampening silicone base.', '["Hot-swappable switches", "Bluetooth 5.3 & 2.4GHz wireless", "CNC Aluminum Unibody", "PBT Dye-Sub Keycaps"]', 1, datetime('now')),
141
+ ('studio-monitor-headphones', 'Studio Master Pro Headphones', 289.0, 'Audio', 4.8, 94, 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800&auto=format&fit=crop&q=80', 'Precision 50mm dynamic drivers engineered for ultra-flat frequency response, critical mixing, and executive focus.', '["Planar Magnetic Drivers", "Memory Foam Cushions", "Detachable Braided Cable", "Hard Travel Case"]', 1, datetime('now')),
142
+ ('ergo-master-mouse', 'ErgoMaster Wireless Precision Mouse', 89.0, 'Peripherals', 4.7, 210, 'https://images.unsplash.com/photo-1615663245857-ac93bb7c39e7?w=800&auto=format&fit=crop&q=80', 'Sculpted ergonomic contour designed to eliminate wrist fatigue during extended development sessions.', '["26,000 DPI Optical Sensor", "70-day Battery Life", "Infinite Magnetic Scroll Wheel", "Silent Click Switches"]', 1, datetime('now')),
143
+ ('walnut-monitor-riser', 'Solid Walnut Monitor Riser Stand', 119.0, 'Desk Setup', 5.0, 62, 'https://images.unsplash.com/photo-1586775490184-b79f0621891f?w=800&auto=format&fit=crop&q=80', 'Handcrafted from single-origin American walnut with cork feet and an integrated aluminum tray for desk accessories.', '["Sustainable American Walnut", "Holds up to 60 lbs", "Integrated Cable Troughs", "Hand-finished Matte Oil"]', 1, datetime('now')),
144
+ ('titanium-desk-pad', 'Matte Slate Vegan Leather Desk Mat', 49.0, 'Desk Setup', 4.6, 175, 'https://images.unsplash.com/photo-1581291518857-4e27b48ff24e?w=800&auto=format&fit=crop&q=80', 'Water-resistant, stain-proof premium desk surface that provides effortless mouse tracking and protects your workspace.', '["Water-resistant PU Leather", "Anti-slip Suede Backing", "90cm x 45cm Large Size", "Easy to Clean"]', 1, datetime('now')),
145
+ ('usb-c-magnetic-cable', '240W Braided Kevlar Charging Cable', 29.0, 'Accessories', 4.9, 340, 'https://images.unsplash.com/photo-1583863788434-e58a36330cf0?w=800&auto=format&fit=crop&q=80', 'Ultra-durable Kevlar-reinforced charging cable supporting up to 240W Power Delivery and 40Gbps Thunderbolt data transfer.', '["240W Power Delivery", "E-Marker Smart Chip", "Braided Kevlar Weave", "2-meter Length"]', 1, datetime('now'));
146
+
147
+ INSERT OR IGNORE INTO orders (id, customer_email, total, status, items_json, created_at) VALUES
148
+ ('ord_demo_101', 'developer@frootkit.dev', 238.0, 'completed', '[{"productId": "nordic-mechanical-keyboard", "quantity": 1}, {"productId": "ergo-master-mouse", "quantity": 1}]', datetime('now'));
149
+ `;
150
+ }
151
+ if (templateId === 'blog') {
152
+ return `
153
+ INSERT OR IGNORE INTO authors (id, name, role, avatar, bio, created_at) VALUES
154
+ ('auth_alex', 'Alex Rivera', 'Staff Systems Architect', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop&crop=faces', 'Pioneering sub-millisecond server rendering and edge runtimes.', datetime('now')),
155
+ ('auth_elena', 'Elena Rostova', 'Design Systems Lead', 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=100&h=100&fit=crop&crop=faces', 'Architect of modern design tokens and component systems.', datetime('now')),
156
+ ('auth_marcus', 'Marcus Chen', 'Core Runtime Engineer', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=faces', 'Optimizing V8 hidden classes and low-latency networking.', datetime('now'));
157
+
158
+ INSERT OR IGNORE INTO posts (id, slug, title, summary, content, category, author_id, read_time, published_at) VALUES
159
+ ('post_welcome', 'welcome-to-frootkit', 'Introducing FrootKit: High-Performance Next.js Alternative', 'Why we combined Preact SSR, native Tailwind CSS v4, and the Froot runtime for maximum developer velocity and sub-millisecond page loads.', '["Modern web frameworks have grown increasingly heavy. Between massive runtime bundles, complex hydration mismatches, and multi-megabyte node_modules dependencies, developers often spend more time debugging tooling than writing features.", "FrootKit takes an unapologetically streamlined approach: sub-millisecond Preact server-side rendering, zero-config Tailwind CSS v4 compilation, and Next.js-style App Router conventions without the heavyweight lock-in.", "By decoupling static site generation from bloated polyfills and leveraging monomorphic V8 dispatch trees, FrootKit achieves sub-millisecond render times while keeping memory usage under 20MB."]', 'Architecture', 'auth_alex', '4 min read', datetime('now')),
160
+ ('post_tailwind', 'tailwind-v4-without-configs', 'Zero Configuration Tailwind CSS v4 in Practice', 'Say goodbye to tailwind.config.js and postcss.config.js. Explore how native CSS @theme variables simplify fullstack design tokens.', '["Tailwind CSS v4 revolutionizes how utility styling is processed. Instead of relying on JavaScript configuration files, everything is declared directly inside modern CSS using the @theme directive.", "In FrootKit, the Tailwind CLI is integrated natively into the development lifecycle. When you run frootkit dev, stylesheets are recompiled in single-digit milliseconds without requiring external bundlers."]', 'Styling', 'auth_elena', '3 min read', datetime('now')),
161
+ ('post_apis', 'building-sub-millisecond-apis', 'Crafting Dynamic Fullstack APIs with Standard Web Standards', 'A deep dive into Next.js-style route handlers powered by standard Request and Response interfaces on top of Froot.', '["Web standards have matured dramatically. Standard Fetch API primitives like Request, Response, Headers, and URL are now universal across Node.js, Bun, Deno, and browsers.", "FrootKit route handlers in src/app/api/**/route.ts take direct advantage of these standards. You export standard HTTP verb functions like GET, POST, PUT, DELETE, and receive native Request objects with zero middleware baggage."]', 'Backend', 'auth_marcus', '5 min read', datetime('now'));
162
+
163
+ INSERT OR IGNORE INTO tags (id, name, slug) VALUES
164
+ ('tag_ssr', 'SSR', 'ssr'),
165
+ ('tag_preact', 'Preact', 'preact'),
166
+ ('tag_perf', 'Performance', 'performance'),
167
+ ('tag_tailwind', 'TailwindCSS', 'tailwindcss');
168
+ `;
169
+ }
170
+ if (templateId === 'auth') {
171
+ return `
172
+ INSERT OR IGNORE INTO users (id, email, password_hash, name, role, created_at) VALUES
173
+ ('usr_admin', 'admin@frootkit.dev', '${ADMIN_PASSWORD_HASH}', 'Sarah Connor', 'Owner', datetime('now')),
174
+ ('usr_marcus', 'marcus@infra.org', '${ADMIN_PASSWORD_HASH}', 'Marcus Chen', 'Developer', datetime('now')),
175
+ ('usr_elena', 'elena@design.io', '${ADMIN_PASSWORD_HASH}', 'Elena Rostova', 'Staff Eng', datetime('now')),
176
+ ('usr_alex', 'alex@systems.net', '${ADMIN_PASSWORD_HASH}', 'Alex Rivera', 'Admin', datetime('now'));
177
+
178
+ INSERT OR IGNORE INTO workspaces (id, name, owner_id, plan, created_at) VALUES
179
+ ('ws_production', 'Orbit Workspace', 'usr_admin', 'Enterprise', datetime('now'));
180
+
181
+ INSERT OR IGNORE INTO activity_logs (id, user_id, action, ip_address, created_at) VALUES
182
+ ('act_1', 'usr_admin', 'Signed in to US-East Production Cluster', '192.168.1.1', datetime('now', '-5 minutes')),
183
+ ('act_2', 'usr_marcus', 'Rotated Web Crypto PBKDF2 signing key', '10.0.0.4', datetime('now', '-2 hours')),
184
+ ('act_3', 'usr_elena', 'Deployed Linear design system tokens', '172.16.0.2', datetime('now', '-1 day')),
185
+ ('act_4', 'usr_alex', 'Provisioned SQLite session replica', '10.0.0.8', datetime('now', '-3 days'));
186
+ `;
187
+ }
188
+ // starter / default
189
+ return `
190
+ INSERT OR IGNORE INTO metrics (id, name, value, unit, recorded_at) VALUES
191
+ ('met_1', 'P99 SSR Latency', '0.12', 'ms', datetime('now')),
192
+ ('met_2', 'Memory Footprint', '14.2', 'MB', datetime('now')),
193
+ ('met_3', 'V8 Cache Hit Ratio', '99.8', '%', datetime('now')),
194
+ ('met_4', 'Uptime', '99.99', '%', datetime('now'));
195
+
196
+ INSERT OR IGNORE INTO system_health (id, status, uptime, latency_ms, checked_at) VALUES
197
+ ('sh_prod', 'healthy', 99.99, 0.12, datetime('now'));
198
+ `;
199
+ }
200
+ /**
201
+ * Directly seeds the physical SQLite database file at scaffold time using node:sqlite.
202
+ */
203
+ export function seedPhysicalDatabase(targetDir, dbOption, templateId) {
204
+ try {
205
+ // Dynamic require so older environments don't fail parsing
206
+ const { DatabaseSync } = require('node:sqlite');
207
+ const dbPath = dbOption === 'prisma'
208
+ ? path.join(targetDir, 'prisma', 'dev.db')
209
+ : path.join(targetDir, 'sqlite.db');
210
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
211
+ const db = new DatabaseSync(dbPath);
212
+ const ddl = getTemplateDdl(templateId);
213
+ const seed = getTemplateSeedSql(templateId);
214
+ if (ddl)
215
+ db.exec(ddl);
216
+ if (seed)
217
+ db.exec(seed);
218
+ db.close();
219
+ }
220
+ catch {
221
+ // Graceful fallback: node:sqlite not supported or file in use
222
+ }
223
+ }
6
224
  export function applyDatabaseAddon(files, dbOption, templateId = 'starter') {
7
225
  if (dbOption === 'none')
8
226
  return;
@@ -41,10 +259,10 @@ const client = createClient({
41
259
 
42
260
  export const db = drizzle(client, { schema });
43
261
  `;
44
- // Initialize the visible SQLite database file inside the project root
262
+ // Initialize physical SQLite file placeholder
45
263
  files['sqlite.db'] = '';
46
264
  if (templateId === 'blog') {
47
- files['src/db/schema.ts'] = `import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
265
+ files['src/db/schema.ts'] = `import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
48
266
 
49
267
  export const authors = sqliteTable('authors', {
50
268
  id: text('id').primaryKey(),
@@ -60,7 +278,7 @@ export const posts = sqliteTable('posts', {
60
278
  slug: text('slug').notNull().unique(),
61
279
  title: text('title').notNull(),
62
280
  summary: text('summary').notNull(),
63
- content: text('content').notNull(), // JSON string or markdown
281
+ content: text('content').notNull(), // JSON string array of paragraphs
64
282
  category: text('category').notNull().default('General'),
65
283
  authorId: text('author_id').references(() => authors.id),
66
284
  readTime: text('read_time').notNull().default('3 min read'),
@@ -80,46 +298,83 @@ export type Author = typeof authors.$inferSelect;
80
298
  import { authors, posts, tags } from './schema.js';
81
299
 
82
300
  async function seed() {
83
- console.log('Seeding blog database...');
84
-
85
- const authorId = 'auth_alex';
86
- await db.insert(authors).values({
87
- id: authorId,
88
- name: 'Alex Rivera',
89
- role: 'Staff Systems Architect',
90
- avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop',
91
- bio: 'Pioneering sub-millisecond server rendering and edge runtimes.',
92
- }).onConflictDoNothing();
301
+ console.log('Seeding blog database with real engineering articles...');
302
+
303
+ await db.insert(authors).values([
304
+ {
305
+ id: 'auth_alex',
306
+ name: 'Alex Rivera',
307
+ role: 'Staff Systems Architect',
308
+ avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop&crop=faces',
309
+ bio: 'Pioneering sub-millisecond server rendering and edge runtimes.',
310
+ },
311
+ {
312
+ id: 'auth_elena',
313
+ name: 'Elena Rostova',
314
+ role: 'Design Systems Lead',
315
+ avatar: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=100&h=100&fit=crop&crop=faces',
316
+ bio: 'Architect of modern design tokens and component systems.',
317
+ },
318
+ {
319
+ id: 'auth_marcus',
320
+ name: 'Marcus Chen',
321
+ role: 'Core Runtime Engineer',
322
+ avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=faces',
323
+ bio: 'Optimizing V8 hidden classes and low-latency networking.',
324
+ },
325
+ ]).onConflictDoNothing();
93
326
 
94
327
  await db.insert(posts).values([
95
328
  {
96
- id: 'post_1',
329
+ id: 'post_welcome',
97
330
  slug: 'welcome-to-frootkit',
98
331
  title: 'Introducing FrootKit: High-Performance Next.js Alternative',
99
- summary: 'Why we combined Preact SSR, native Tailwind CSS v4, and the Froot runtime for maximum velocity.',
332
+ summary: 'Why we combined Preact SSR, native Tailwind CSS v4, and the Froot runtime for maximum developer velocity and sub-millisecond page loads.',
100
333
  content: JSON.stringify([
101
- 'Modern web frameworks have grown increasingly heavy.',
102
- 'FrootKit provides sub-millisecond server rendering with zero configuration.',
334
+ 'Modern web frameworks have grown increasingly heavy. Between massive runtime bundles, complex hydration mismatches, and multi-megabyte node_modules dependencies, developers often spend more time debugging tooling than writing features.',
335
+ 'FrootKit takes an unapologetically streamlined approach: sub-millisecond Preact server-side rendering, zero-config Tailwind CSS v4 compilation, and Next.js-style App Router conventions without the heavyweight lock-in.',
336
+ 'By decoupling static site generation from bloated polyfills and leveraging monomorphic V8 dispatch trees, FrootKit achieves sub-millisecond render times while keeping memory usage under 20MB.',
103
337
  ]),
104
338
  category: 'Architecture',
105
- authorId,
339
+ authorId: 'auth_alex',
106
340
  readTime: '4 min read',
107
341
  },
108
342
  {
109
- id: 'post_2',
343
+ id: 'post_tailwind',
110
344
  slug: 'tailwind-v4-without-configs',
111
345
  title: 'Zero Configuration Tailwind CSS v4 in Practice',
112
- summary: 'Explore how native CSS @theme variables simplify fullstack design tokens.',
346
+ summary: 'Say goodbye to tailwind.config.js and postcss.config.js. Explore how native CSS @theme variables simplify fullstack design tokens.',
113
347
  content: JSON.stringify([
114
- 'Tailwind CSS v4 redefines utility styling directly inside CSS without js config files.',
348
+ 'Tailwind CSS v4 revolutionizes how utility styling is processed. Instead of relying on JavaScript configuration files, everything is declared directly inside modern CSS using the @theme directive.',
349
+ 'In FrootKit, the Tailwind CLI is integrated natively into the development lifecycle. When you run frootkit dev, stylesheets are recompiled in single-digit milliseconds without requiring external bundlers.',
115
350
  ]),
116
351
  category: 'Styling',
117
- authorId,
352
+ authorId: 'auth_elena',
118
353
  readTime: '3 min read',
119
354
  },
355
+ {
356
+ id: 'post_apis',
357
+ slug: 'building-sub-millisecond-apis',
358
+ title: 'Crafting Dynamic Fullstack APIs with Standard Web Standards',
359
+ summary: 'A deep dive into Next.js-style route handlers powered by standard Request and Response interfaces on top of Froot.',
360
+ content: JSON.stringify([
361
+ 'Web standards have matured dramatically. Standard Fetch API primitives like Request, Response, Headers, and URL are now universal across Node.js, Bun, Deno, and browsers.',
362
+ 'FrootKit route handlers in src/app/api/**/route.ts take direct advantage of these standards. You export standard HTTP verb functions like GET, POST, PUT, DELETE, and receive native Request objects with zero middleware baggage.',
363
+ ]),
364
+ category: 'Backend',
365
+ authorId: 'auth_marcus',
366
+ readTime: '5 min read',
367
+ },
120
368
  ]).onConflictDoNothing();
121
369
 
122
- console.log('✓ Seeding complete!');
370
+ await db.insert(tags).values([
371
+ { id: 'tag_ssr', name: 'SSR', slug: 'ssr' },
372
+ { id: 'tag_preact', name: 'Preact', slug: 'preact' },
373
+ { id: 'tag_perf', name: 'Performance', slug: 'performance' },
374
+ { id: 'tag_tailwind', name: 'TailwindCSS', slug: 'tailwindcss' },
375
+ ]).onConflictDoNothing();
376
+
377
+ console.log('✓ Seeding complete! 3 articles and authors populated.');
123
378
  }
124
379
 
125
380
  seed().catch(console.error);
@@ -162,10 +417,10 @@ export type Product = typeof products.$inferSelect;
162
417
  export type Order = typeof orders.$inferSelect;
163
418
  `;
164
419
  files['src/db/seed.ts'] = `import { db } from './index.js';
165
- import { products } from './schema.js';
420
+ import { products, orders } from './schema.js';
166
421
 
167
422
  async function seed() {
168
- console.log('Seeding e-commerce store database...');
423
+ console.log('Seeding e-commerce store database with all 6 tactile instruments...');
169
424
 
170
425
  await db.insert(products).values([
171
426
  {
@@ -175,9 +430,9 @@ async function seed() {
175
430
  category: 'Keyboards',
176
431
  rating: 4.9,
177
432
  reviewsCount: 128,
178
- image: 'https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=600&h=450&fit=crop',
179
- description: 'Aircraft-grade anodized aluminum body with custom hot-swappable linear switches.',
180
- features: JSON.stringify(['Hot-swappable switches', 'Bluetooth 5.3', 'CNC Aluminum Chassis']),
433
+ image: 'https://images.unsplash.com/photo-1595225476474-87563907a212?w=800&auto=format&fit=crop&q=80',
434
+ description: 'Aircraft-grade CNC anodized aluminum chassis with custom factory-lubed linear switches and sound-dampening silicone base.',
435
+ features: JSON.stringify(['Hot-swappable switches', 'Bluetooth 5.3 & 2.4GHz wireless', 'CNC Aluminum Unibody', 'PBT Dye-Sub Keycaps']),
181
436
  inStock: true,
182
437
  },
183
438
  {
@@ -187,14 +442,70 @@ async function seed() {
187
442
  category: 'Audio',
188
443
  rating: 4.8,
189
444
  reviewsCount: 94,
190
- image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600&h=450&fit=crop',
191
- description: 'Precision 50mm dynamic drivers engineered for ultra-flat frequency response.',
192
- features: JSON.stringify(['Planar Magnetic Sound', 'Memory Foam Cushions', 'Detachable Silver Cable']),
445
+ image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800&auto=format&fit=crop&q=80',
446
+ description: 'Precision 50mm dynamic drivers engineered for ultra-flat frequency response, critical mixing, and executive focus.',
447
+ features: JSON.stringify(['Planar Magnetic Drivers', 'Memory Foam Cushions', 'Detachable Braided Cable', 'Hard Travel Case']),
448
+ inStock: true,
449
+ },
450
+ {
451
+ id: 'ergo-master-mouse',
452
+ name: 'ErgoMaster Wireless Precision Mouse',
453
+ price: 89.0,
454
+ category: 'Peripherals',
455
+ rating: 4.7,
456
+ reviewsCount: 210,
457
+ image: 'https://images.unsplash.com/photo-1615663245857-ac93bb7c39e7?w=800&auto=format&fit=crop&q=80',
458
+ description: 'Sculpted ergonomic contour designed to eliminate wrist fatigue during extended development sessions.',
459
+ features: JSON.stringify(['26,000 DPI Optical Sensor', '70-day Battery Life', 'Infinite Magnetic Scroll Wheel', 'Silent Click Switches']),
460
+ inStock: true,
461
+ },
462
+ {
463
+ id: 'walnut-monitor-riser',
464
+ name: 'Solid Walnut Monitor Riser Stand',
465
+ price: 119.0,
466
+ category: 'Desk Setup',
467
+ rating: 5.0,
468
+ reviewsCount: 62,
469
+ image: 'https://images.unsplash.com/photo-1586775490184-b79f0621891f?w=800&auto=format&fit=crop&q=80',
470
+ description: 'Handcrafted from single-origin American walnut with cork feet and an integrated aluminum tray for desk accessories.',
471
+ features: JSON.stringify(['Sustainable American Walnut', 'Holds up to 60 lbs', 'Integrated Cable Troughs', 'Hand-finished Matte Oil']),
472
+ inStock: true,
473
+ },
474
+ {
475
+ id: 'titanium-desk-pad',
476
+ name: 'Matte Slate Vegan Leather Desk Mat',
477
+ price: 49.0,
478
+ category: 'Desk Setup',
479
+ rating: 4.6,
480
+ reviewsCount: 175,
481
+ image: 'https://images.unsplash.com/photo-1581291518857-4e27b48ff24e?w=800&auto=format&fit=crop&q=80',
482
+ description: 'Water-resistant, stain-proof premium desk surface that provides effortless mouse tracking and protects your workspace.',
483
+ features: JSON.stringify(['Water-resistant PU Leather', 'Anti-slip Suede Backing', '90cm x 45cm Large Size', 'Easy to Clean']),
484
+ inStock: true,
485
+ },
486
+ {
487
+ id: 'usb-c-magnetic-cable',
488
+ name: '240W Braided Kevlar Charging Cable',
489
+ price: 29.0,
490
+ category: 'Accessories',
491
+ rating: 4.9,
492
+ reviewsCount: 340,
493
+ image: 'https://images.unsplash.com/photo-1583863788434-e58a36330cf0?w=800&auto=format&fit=crop&q=80',
494
+ description: 'Ultra-durable Kevlar-reinforced charging cable supporting up to 240W Power Delivery and 40Gbps Thunderbolt data transfer.',
495
+ features: JSON.stringify(['240W Power Delivery', 'E-Marker Smart Chip', 'Braided Kevlar Weave', '2-meter Length']),
193
496
  inStock: true,
194
497
  },
195
498
  ]).onConflictDoNothing();
196
499
 
197
- console.log('✓ Seeding complete!');
500
+ await db.insert(orders).values({
501
+ id: 'ord_demo_101',
502
+ customerEmail: 'developer@frootkit.dev',
503
+ total: 238.0,
504
+ status: 'completed',
505
+ itemsJson: JSON.stringify([{ productId: 'nordic-mechanical-keyboard', quantity: 1 }, { productId: 'ergo-master-mouse', quantity: 1 }]),
506
+ }).onConflictDoNothing();
507
+
508
+ console.log('✓ Seeding complete! All 6 products inserted.');
198
509
  }
199
510
 
200
511
  seed().catch(console.error);
@@ -228,34 +539,93 @@ export const workspaces = sqliteTable('workspaces', {
228
539
  createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
229
540
  });
230
541
 
542
+ export const activityLogs = sqliteTable('activity_logs', {
543
+ id: text('id').primaryKey(),
544
+ userId: text('user_id').notNull().references(() => users.id),
545
+ action: text('action').notNull(),
546
+ ipAddress: text('ip_address'),
547
+ createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
548
+ });
549
+
231
550
  export type User = typeof users.$inferSelect;
232
551
  export type Session = typeof sessions.$inferSelect;
552
+ export type Workspace = typeof workspaces.$inferSelect;
553
+ export type ActivityLog = typeof activityLogs.$inferSelect;
233
554
  `;
234
555
  files['src/db/seed.ts'] = `import { db } from './index.js';
235
- import { users, workspaces } from './schema.js';
236
- import { hashPassword } from '../lib/auth.js';
556
+ import { users, workspaces, activityLogs } from './schema.js';
237
557
 
238
558
  async function seed() {
239
- console.log('Seeding SaaS database with demo user...');
559
+ console.log('Seeding SaaS database with team members and admin account...');
240
560
 
241
- const passwordHash = await hashPassword('password123');
242
- const adminId = 'usr_admin_demo';
561
+ // Standard pre-hashed PBKDF2 hash of "password123"
562
+ const passwordHash = '${ADMIN_PASSWORD_HASH}';
243
563
 
244
- await db.insert(users).values({
245
- id: adminId,
246
- email: 'admin@frootkit.dev',
247
- passwordHash,
248
- name: 'Sarah Connor',
249
- role: 'admin',
250
- }).onConflictDoNothing();
564
+ await db.insert(users).values([
565
+ {
566
+ id: 'usr_admin',
567
+ email: 'admin@frootkit.dev',
568
+ passwordHash,
569
+ name: 'Sarah Connor',
570
+ role: 'Owner',
571
+ },
572
+ {
573
+ id: 'usr_marcus',
574
+ email: 'marcus@infra.org',
575
+ passwordHash,
576
+ name: 'Marcus Chen',
577
+ role: 'Developer',
578
+ },
579
+ {
580
+ id: 'usr_elena',
581
+ email: 'elena@design.io',
582
+ passwordHash,
583
+ name: 'Elena Rostova',
584
+ role: 'Staff Eng',
585
+ },
586
+ {
587
+ id: 'usr_alex',
588
+ email: 'alex@systems.net',
589
+ passwordHash,
590
+ name: 'Alex Rivera',
591
+ role: 'Admin',
592
+ },
593
+ ]).onConflictDoNothing();
251
594
 
252
595
  await db.insert(workspaces).values({
253
- id: 'ws_demo_01',
254
- name: "Sarah's Production Workspace",
255
- ownerId: adminId,
256
- plan: 'enterprise',
596
+ id: 'ws_production',
597
+ name: 'Orbit Workspace',
598
+ ownerId: 'usr_admin',
599
+ plan: 'Enterprise',
257
600
  }).onConflictDoNothing();
258
601
 
602
+ await db.insert(activityLogs).values([
603
+ {
604
+ id: 'act_1',
605
+ userId: 'usr_admin',
606
+ action: 'Signed in to US-East Production Cluster',
607
+ ipAddress: '192.168.1.1',
608
+ },
609
+ {
610
+ id: 'act_2',
611
+ userId: 'usr_marcus',
612
+ action: 'Rotated Web Crypto PBKDF2 signing key',
613
+ ipAddress: '10.0.0.4',
614
+ },
615
+ {
616
+ id: 'act_3',
617
+ userId: 'usr_elena',
618
+ action: 'Deployed Linear design system tokens',
619
+ ipAddress: '172.16.0.2',
620
+ },
621
+ {
622
+ id: 'act_4',
623
+ userId: 'usr_alex',
624
+ action: 'Provisioned SQLite session replica',
625
+ ipAddress: '10.0.0.8',
626
+ },
627
+ ]).onConflictDoNothing();
628
+
259
629
  console.log('✓ Seeding complete! Demo account created: admin@frootkit.dev / password123');
260
630
  }
261
631
 
@@ -264,30 +634,46 @@ seed().catch(console.error);
264
634
  }
265
635
  else {
266
636
  // Starter / default
267
- files['src/db/schema.ts'] = `import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
637
+ files['src/db/schema.ts'] = `import { sqliteTable, text, real } from 'drizzle-orm/sqlite-core';
268
638
 
269
- export const users = sqliteTable('users', {
639
+ export const metrics = sqliteTable('metrics', {
270
640
  id: text('id').primaryKey(),
271
641
  name: text('name').notNull(),
272
- email: text('email').notNull().unique(),
273
- role: text('role').notNull().default('user'),
274
- createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
642
+ value: text('value').notNull(),
643
+ unit: text('unit').notNull(),
644
+ recordedAt: text('recorded_at').notNull().$defaultFn(() => new Date().toISOString()),
275
645
  });
276
646
 
277
- export type User = typeof users.$inferSelect;
278
- export type NewUser = typeof users.$inferInsert;
647
+ export const systemHealth = sqliteTable('system_health', {
648
+ id: text('id').primaryKey(),
649
+ status: text('status').notNull(),
650
+ uptime: real('uptime').notNull(),
651
+ latencyMs: real('latency_ms').notNull(),
652
+ checkedAt: text('checked_at').notNull().$defaultFn(() => new Date().toISOString()),
653
+ });
654
+
655
+ export type Metric = typeof metrics.$inferSelect;
656
+ export type SystemHealth = typeof systemHealth.$inferSelect;
279
657
  `;
280
658
  files['src/db/seed.ts'] = `import { db } from './index.js';
281
- import { users } from './schema.js';
659
+ import { metrics, systemHealth } from './schema.js';
282
660
 
283
661
  async function seed() {
284
- console.log('Seeding database...');
285
- await db.insert(users).values({
286
- id: 'usr_starter_1',
287
- name: 'Developer',
288
- email: 'developer@frootkit.dev',
289
- role: 'admin',
662
+ console.log('Seeding starter telemetry metrics...');
663
+ await db.insert(metrics).values([
664
+ { id: 'met_1', name: 'P99 SSR Latency', value: '0.12', unit: 'ms' },
665
+ { id: 'met_2', name: 'Memory Footprint', value: '14.2', unit: 'MB' },
666
+ { id: 'met_3', name: 'V8 Cache Hit Ratio', value: '99.8', unit: '%' },
667
+ { id: 'met_4', name: 'Cluster Uptime', value: '99.99', unit: '%' },
668
+ ]).onConflictDoNothing();
669
+
670
+ await db.insert(systemHealth).values({
671
+ id: 'sh_prod',
672
+ status: 'healthy',
673
+ uptime: 99.99,
674
+ latencyMs: 0.12,
290
675
  }).onConflictDoNothing();
676
+
291
677
  console.log('✓ Seeding complete!');
292
678
  }
293
679
 
@@ -306,7 +692,6 @@ seed().catch(console.error);
306
692
 
307
693
  export const prisma = new PrismaClient();
308
694
  `;
309
- // Initialize the visible Prisma SQLite database file inside prisma/ directory
310
695
  files['prisma/dev.db'] = '';
311
696
  if (templateId === 'blog') {
312
697
  files['prisma/schema.prisma'] = `datasource db {
@@ -319,7 +704,7 @@ generator client {
319
704
  }
320
705
 
321
706
  model Author {
322
- id String @id @default(uuid())
707
+ id String @id
323
708
  name String
324
709
  role String
325
710
  avatar String
@@ -329,7 +714,7 @@ model Author {
329
714
  }
330
715
 
331
716
  model Post {
332
- id String @id @default(uuid())
717
+ id String @id
333
718
  slug String @unique
334
719
  title String
335
720
  summary String
@@ -342,7 +727,7 @@ model Post {
342
727
  }
343
728
 
344
729
  model Tag {
345
- id String @id @default(uuid())
730
+ id String @id
346
731
  name String @unique
347
732
  slug String @unique
348
733
  }
@@ -351,14 +736,14 @@ model Tag {
351
736
 
352
737
  async function seed() {
353
738
  console.log('Seeding blog database with Prisma...');
354
- const author = await prisma.author.upsert({
739
+ await prisma.author.upsert({
355
740
  where: { id: 'auth_alex' },
356
741
  update: {},
357
742
  create: {
358
743
  id: 'auth_alex',
359
744
  name: 'Alex Rivera',
360
745
  role: 'Staff Systems Architect',
361
- avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop',
746
+ avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&h=100&fit=crop&crop=faces',
362
747
  bio: 'Pioneering sub-millisecond server rendering.',
363
748
  },
364
749
  });
@@ -367,11 +752,12 @@ async function seed() {
367
752
  where: { slug: 'welcome-to-frootkit' },
368
753
  update: {},
369
754
  create: {
755
+ id: 'post_welcome',
370
756
  slug: 'welcome-to-frootkit',
371
757
  title: 'Introducing FrootKit: High-Performance Next.js Alternative',
372
758
  summary: 'Why we combined Preact SSR and Tailwind CSS v4.',
373
- content: 'FrootKit provides sub-millisecond server rendering.',
374
- authorId: author.id,
759
+ content: JSON.stringify(['Modern web frameworks have grown increasingly heavy.', 'FrootKit provides sub-millisecond server rendering with zero configuration.']),
760
+ authorId: 'auth_alex',
375
761
  category: 'Architecture',
376
762
  },
377
763
  });
@@ -419,22 +805,82 @@ model Order {
419
805
 
420
806
  async function seed() {
421
807
  console.log('Seeding e-commerce store with Prisma...');
422
- await prisma.product.upsert({
423
- where: { id: 'nordic-mechanical-keyboard' },
424
- update: {},
425
- create: {
808
+ const items = [
809
+ {
426
810
  id: 'nordic-mechanical-keyboard',
427
811
  name: 'Nordic Minimal Mechanical Keyboard',
428
812
  price: 149.0,
429
813
  category: 'Keyboards',
430
814
  rating: 4.9,
431
815
  reviewsCount: 128,
432
- image: 'https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=600&h=450&fit=crop',
433
- description: 'Aircraft-grade anodized aluminum body with custom hot-swappable linear switches.',
434
- features: '["Hot-swappable switches", "Bluetooth 5.3", "CNC Aluminum Chassis"]',
435
- inStock: true,
816
+ image: 'https://images.unsplash.com/photo-1595225476474-87563907a212?w=800&auto=format&fit=crop&q=80',
817
+ description: 'Aircraft-grade CNC anodized aluminum chassis with custom factory-lubed linear switches.',
818
+ features: JSON.stringify(['Hot-swappable switches', 'Bluetooth 5.3 & 2.4GHz wireless']),
436
819
  },
437
- });
820
+ {
821
+ id: 'studio-monitor-headphones',
822
+ name: 'Studio Master Pro Headphones',
823
+ price: 289.0,
824
+ category: 'Audio',
825
+ rating: 4.8,
826
+ reviewsCount: 94,
827
+ image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800&auto=format&fit=crop&q=80',
828
+ description: 'Precision 50mm dynamic drivers engineered for ultra-flat frequency response.',
829
+ features: JSON.stringify(['Planar Magnetic Drivers', 'Memory Foam Cushions']),
830
+ },
831
+ {
832
+ id: 'ergo-master-mouse',
833
+ name: 'ErgoMaster Wireless Precision Mouse',
834
+ price: 89.0,
835
+ category: 'Peripherals',
836
+ rating: 4.7,
837
+ reviewsCount: 210,
838
+ image: 'https://images.unsplash.com/photo-1615663245857-ac93bb7c39e7?w=800&auto=format&fit=crop&q=80',
839
+ description: 'Sculpted ergonomic contour designed to eliminate wrist fatigue during extended development sessions.',
840
+ features: JSON.stringify(['26,000 DPI Optical Sensor', '70-day Battery Life']),
841
+ },
842
+ {
843
+ id: 'walnut-monitor-riser',
844
+ name: 'Solid Walnut Monitor Riser Stand',
845
+ price: 119.0,
846
+ category: 'Desk Setup',
847
+ rating: 5.0,
848
+ reviewsCount: 62,
849
+ image: 'https://images.unsplash.com/photo-1586775490184-b79f0621891f?w=800&auto=format&fit=crop&q=80',
850
+ description: 'Handcrafted from single-origin American walnut with cork feet and an integrated aluminum tray.',
851
+ features: JSON.stringify(['Sustainable American Walnut', 'Holds up to 60 lbs']),
852
+ },
853
+ {
854
+ id: 'titanium-desk-pad',
855
+ name: 'Matte Slate Vegan Leather Desk Mat',
856
+ price: 49.0,
857
+ category: 'Desk Setup',
858
+ rating: 4.6,
859
+ reviewsCount: 175,
860
+ image: 'https://images.unsplash.com/photo-1581291518857-4e27b48ff24e?w=800&auto=format&fit=crop&q=80',
861
+ description: 'Water-resistant, stain-proof premium desk surface that provides effortless mouse tracking.',
862
+ features: JSON.stringify(['Water-resistant PU Leather', '90cm x 45cm Large Size']),
863
+ },
864
+ {
865
+ id: 'usb-c-magnetic-cable',
866
+ name: '240W Braided Kevlar Charging Cable',
867
+ price: 29.0,
868
+ category: 'Accessories',
869
+ rating: 4.9,
870
+ reviewsCount: 340,
871
+ image: 'https://images.unsplash.com/photo-1583863788434-e58a36330cf0?w=800&auto=format&fit=crop&q=80',
872
+ description: 'Ultra-durable Kevlar-reinforced charging cable supporting up to 240W Power Delivery.',
873
+ features: JSON.stringify(['240W Power Delivery', 'Braided Kevlar Weave']),
874
+ },
875
+ ];
876
+
877
+ for (const item of items) {
878
+ await prisma.product.upsert({
879
+ where: { id: item.id },
880
+ update: {},
881
+ create: item,
882
+ });
883
+ }
438
884
  console.log('✓ Seeding complete!');
439
885
  }
440
886
 
@@ -481,11 +927,10 @@ model Workspace {
481
927
  }
482
928
  `;
483
929
  files['src/db/seed.ts'] = `import { prisma } from './index.js';
484
- import { hashPassword } from '../lib/auth.js';
485
930
 
486
931
  async function seed() {
487
932
  console.log('Seeding auth database with Prisma...');
488
- const passwordHash = await hashPassword('password123');
933
+ const passwordHash = '${ADMIN_PASSWORD_HASH}';
489
934
 
490
935
  await prisma.user.upsert({
491
936
  where: { email: 'admin@frootkit.dev' },
@@ -494,7 +939,7 @@ async function seed() {
494
939
  email: 'admin@frootkit.dev',
495
940
  passwordHash,
496
941
  name: 'Sarah Connor',
497
- role: 'admin',
942
+ role: 'Owner',
498
943
  },
499
944
  });
500
945
 
@@ -514,25 +959,26 @@ generator client {
514
959
  provider = "prisma-client-js"
515
960
  }
516
961
 
517
- model User {
518
- id String @id @default(uuid())
519
- email String @unique
520
- name String
521
- role String @default("user")
522
- createdAt DateTime @default(now())
962
+ model Metric {
963
+ id String @id
964
+ name String
965
+ value String
966
+ unit String
967
+ recordedAt DateTime @default(now())
523
968
  }
524
969
  `;
525
970
  files['src/db/seed.ts'] = `import { prisma } from './index.js';
526
971
 
527
972
  async function seed() {
528
973
  console.log('Seeding starter database with Prisma...');
529
- await prisma.user.upsert({
530
- where: { email: 'developer@frootkit.dev' },
974
+ await prisma.metric.upsert({
975
+ where: { id: 'met_1' },
531
976
  update: {},
532
977
  create: {
533
- email: 'developer@frootkit.dev',
534
- name: 'Developer',
535
- role: 'admin',
978
+ id: 'met_1',
979
+ name: 'P99 SSR Latency',
980
+ value: '0.12',
981
+ unit: 'ms',
536
982
  },
537
983
  });
538
984
  console.log('✓ Seeding complete!');