cosmolo 0.3.9 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,6 +24,7 @@ a canonical "just add Markdown and go" story without asking you to leave.
24
24
  | Component in Markdown | Yes (.svx) | Yes (.mdx) | Yes | No |
25
25
  | Config-driven categories | Yes | No | No | No |
26
26
  | Headless CMS (JSON API) | Yes | Manual | Manual | Manual |
27
+ | DB migration path | `migrate:db` | No | No | Manual |
27
28
  | Learning curve | SvelteKit only | Astro concepts | Vue + Nuxt | SvelteKit only |
28
29
 
29
30
  **Core principles:**
@@ -53,7 +54,7 @@ bun generate:article
53
54
  bun dev
54
55
  ```
55
56
 
56
- `cosmolo init` asks two questions — which mode (full UI or server-only) and which adapter (SSG or serverless) — then copies the appropriate route files into your project.
57
+ `cosmolo init` asks two questions — which mode (Full or Slim) and which adapter (SSG, Cloudflare, or Serverless) — then copies the appropriate route files into your project.
57
58
 
58
59
  ---
59
60
 
@@ -272,6 +273,160 @@ Prompts for key (slug), label, and description. Appends the new entry to `config
272
273
 
273
274
  ---
274
275
 
276
+ ## Cloudflare
277
+
278
+ Cosmolo works with any SvelteKit-compatible deployment platform, but it is purpose-built
279
+ around the Cloudflare stack. SvelteKit and Cloudflare Workers are an unusually good fit —
280
+ edge-native rendering, zero cold starts, globally distributed infrastructure, and a generous
281
+ free tier. Cosmolo's CLI removes the usual setup friction so you can go from `init` to
282
+ deployed in minutes.
283
+
284
+ ### One-command Cloudflare setup
285
+
286
+ ```bash
287
+ bunx cosmolo init # choose "Cloudflare" when prompted for adapter
288
+ ```
289
+
290
+ This single command generates everything needed to deploy:
291
+
292
+ | Generated file | Purpose |
293
+ |---|---|
294
+ | `svelte.config.js` | Pre-configured with `adapter-cloudflare` |
295
+ | `wrangler.toml` | Project name, `nodejs_compat`, D1 template commented out |
296
+ | `src/app.d.ts` | `App.Platform` with `Env`, `CfProperties`, `ExecutionContext` |
297
+ | `.github/workflows/deploy.yml` | Optional — push-to-`main` deploy via `wrangler-action` |
298
+
299
+ After init, two commands to go live:
300
+
301
+ ```bash
302
+ bun install && bun add -D @sveltejs/adapter-cloudflare @cloudflare/workers-types
303
+ bun run deploy # bun run build + wrangler pages deploy
304
+ ```
305
+
306
+ If you opted in to GitHub Actions during init, pushing to `main` triggers the deploy automatically.
307
+
308
+ ### Cloudflare services
309
+
310
+ | Command | What it sets up |
311
+ |---|---|
312
+ | `cosmolo migrate:db` → option 3 | **D1** — Drizzle schema, CRUD helpers (`getArticlesByCategory`, `getArticlesByTag`, …), D1-backed `+page.server.ts` route files |
313
+ | `cosmolo setup:r2` | **R2** — `wrangler.toml` binding, `src/lib/r2.ts` helper, `/assets/[...key]` edge serving route |
314
+
315
+ Each command is self-contained — run only the ones you need.
316
+
317
+ ---
318
+
319
+ ## Database Migration
320
+
321
+ When a file-based Cosmolo site outgrows Markdown — multiple writers, mobile editing,
322
+ a growing team — `migrate:db` converts your content to a database without rewriting
323
+ your application code.
324
+
325
+ DB support is optional. File-based sites continue to work exactly as before.
326
+ Migration is a one-time operation when you're ready to scale.
327
+
328
+ ```bash
329
+ bunx cosmolo migrate:db
330
+ ```
331
+
332
+ The command is interactive and offers three paths:
333
+
334
+ | Option | Description |
335
+ |---|---|
336
+ | **1 — Export SQL files** | Generates `cosmolo-migration/*.sql` (CREATE TABLE + INSERT for all articles and categories). Works with any relational database. |
337
+ | **2 — Execute directly** | Executes the same SQL against a local SQLite database. Set `DATABASE_URL=./mysite.db` before running. |
338
+ | **3 — Drizzle + Cloudflare D1** | Full setup: generates Drizzle schema, CRUD helpers, `wrangler.toml` D1 binding, and `drizzle.config.ts`. Runs preflight checks before writing anything. |
339
+
340
+ ### What gets migrated
341
+
342
+ - **Articles** — all frontmatter fields plus the raw Markdown body. Subdirectory-organized files (e.g. `articles/2024/post.md`) are handled automatically; the slug becomes `2024/post`.
343
+ - **Categories** — from `config/categories.json`
344
+ - **Draft articles** — included in the DB with `draft = 1`; the generated `getArticles()` helper filters them out automatically
345
+
346
+ ### Option 3 — Drizzle + Cloudflare D1
347
+
348
+ After a preflight check (drizzle installed, wrangler.toml, table conflicts), the following files are generated:
349
+
350
+ ```
351
+ drizzle/schema.ts ← Drizzle schema for articles and categories tables
352
+ src/lib/db/articles.ts ← getArticles, getArticlesByCategory, getArticlesByTag,
353
+ getArticle, parseArticle, createArticle, updateArticle, deleteArticle
354
+ src/lib/db/categories.ts ← getCategories, getCategory, createCategory, updateCategory, deleteCategory
355
+ wrangler.toml ← [[d1_databases]] binding added (merged if file exists)
356
+ drizzle.config.ts ← drizzle-kit config (dialect: sqlite)
357
+ .dev.vars.example ← Cloudflare environment variable reference
358
+ ```
359
+
360
+ Optionally (prompted during setup), the existing `+page.server.ts` route files are replaced with D1-backed versions that read from `platform.env.DB` instead of the Cosmolo virtual module:
361
+
362
+ ```
363
+ src/routes/+page.server.ts ← Home page — getArticles + getCategories from D1
364
+ src/routes/articles/[slug]/+page.server.ts ← Article — getArticle from D1, Markdown rendered with marked
365
+ src/routes/categories/[slug]/+page.server.ts ← Category — getArticlesByCategory from D1
366
+ src/routes/tags/[tag]/+page.server.ts ← Tag — getArticlesByTag from D1 (json_each query)
367
+ ```
368
+
369
+ The command prints step-by-step instructions after generation:
370
+ 1. `bunx wrangler d1 create <db-name>` and copy the `database_id` into `wrangler.toml`
371
+ 2. `bunx drizzle-kit generate` to create SQL migration files
372
+ 3. `bunx wrangler d1 migrations apply <db-name> --local` to apply locally
373
+ 4. Run Option 1 to export seed SQL, then `wrangler d1 execute` to import your articles
374
+ 5. `bun add -d @cloudflare/workers-types` for TypeScript support
375
+ 6. Add `interface Platform { env: { DB: D1Database } }` to `src/app.d.ts`
376
+
377
+ ### SSR requirement
378
+
379
+ DB-backed content requires a server-capable adapter. Content in D1 is resolved at
380
+ request time, so `adapter-static` (SSG) is not compatible.
381
+
382
+ Switch to `adapter-cloudflare` for Cloudflare Pages, or `adapter-node` for a
383
+ self-hosted server:
384
+
385
+ ```diff
386
+ - import adapter from '@sveltejs/adapter-static';
387
+ + import adapter from '@sveltejs/adapter-cloudflare';
388
+ ```
389
+
390
+ The upside: content edits take effect immediately — no rebuild or redeploy needed.
391
+
392
+ ---
393
+
394
+ ## R2 Asset Storage
395
+
396
+ Add Cloudflare R2 object storage for article images and other binary assets:
397
+
398
+ ```bash
399
+ bunx cosmolo setup:r2
400
+ ```
401
+
402
+ The command asks for a bucket name and binding name, then generates:
403
+
404
+ ```
405
+ src/lib/r2.ts ← getR2Asset(bucket, key) helper
406
+ src/routes/assets/[...key]/+server.ts ← Edge route — serves files directly from R2
407
+ wrangler.toml ← [[r2_buckets]] binding appended
408
+ ```
409
+
410
+ After setup:
411
+
412
+ ```bash
413
+ # 1. Create the bucket
414
+ bunx wrangler r2 bucket create <bucket-name>
415
+
416
+ # 2. Upload an asset
417
+ bunx wrangler r2 object put <bucket-name>/images/photo.jpg --file ./static/images/photo.jpg
418
+
419
+ # 3. Reference it in templates as /assets/images/photo.jpg
420
+ ```
421
+
422
+ Add the binding type to `src/app.d.ts` (one line):
423
+
424
+ ```ts
425
+ interface Platform { env: { ASSETS: R2Bucket } }
426
+ ```
427
+
428
+ ---
429
+
275
430
  ## Headless CMS
276
431
 
277
432
  Cosmolo can expose your content as static JSON endpoints, making it usable as a
@@ -325,7 +480,7 @@ bun add -D vite @sveltejs/kit
325
480
  **1. Create `cosmolo.config.ts`** in your project root
326
481
 
327
482
  ```typescript
328
- import { resolveConfig } from 'cosmolo';
483
+ import { resolveConfig } from 'cosmolo/plugin';
329
484
 
330
485
  export default resolveConfig({
331
486
  articlesDir: 'src/content/articles', // default
@@ -373,10 +528,11 @@ The command asks two questions:
373
528
 
374
529
  **Adapter**
375
530
 
376
- | Adapter | Effect |
531
+ | Adapter | Generated files |
377
532
  |---|---|
378
- | **SSG** (`adapter-static`) | Also creates `src/routes/+layout.ts` with `export const prerender = true` |
379
- | **Serverless / SSR** | No layout file routes render on demand |
533
+ | **SSG** (`adapter-static`) | `svelte.config.js` (adapter-static) + `src/routes/+layout.ts` (`prerender = true`) |
534
+ | **Cloudflare** (`adapter-cloudflare`) | `svelte.config.js` (adapter-cloudflare) + `wrangler.toml` + `src/app.d.ts` (`Platform` type). Optionally `.github/workflows/deploy.yml`. |
535
+ | **Serverless** | No extra files — bring your own adapter (Vercel, Node, etc.) |
380
536
 
381
537
  If any target file already exists, the command lists every conflict and exits without
382
538
  writing anything.
package/dist/cli/index.js CHANGED
@@ -7,14 +7,22 @@ switch (cmd) {
7
7
  case 'generate':
8
8
  await (await import('./generate.js')).main();
9
9
  break;
10
+ case 'migrate:db':
11
+ await (await import('./migrate.js')).main();
12
+ break;
13
+ case 'setup:r2':
14
+ await (await import('./setup/r2.js')).main();
15
+ break;
10
16
  default: {
11
17
  const isUnknown = Boolean(cmd);
12
18
  if (isUnknown)
13
19
  console.error(`Unknown command: ${cmd}\n`);
14
20
  console.log('Usage: cosmolo <command>\n');
15
21
  console.log('Commands:');
16
- console.log(' init Scaffold routes into an existing SvelteKit project');
22
+ console.log(' init Scaffold routes and config into a SvelteKit project');
17
23
  console.log(' generate [article|page|category] Create content files');
24
+ console.log(' migrate:db Migrate file-based content to a database (D1)');
25
+ console.log(' setup:r2 Add Cloudflare R2 bucket for asset storage');
18
26
  process.exit(isUnknown ? 1 : 0);
19
27
  }
20
28
  }
package/dist/cli/init.js CHANGED
@@ -36,7 +36,21 @@ function destPath(relativePath, projectRoot) {
36
36
  .replace(/^lib\//, 'src/lib/');
37
37
  return path.join(projectRoot, mapped);
38
38
  }
39
- function injectPackageScripts(projectRoot) {
39
+ function readProjectName(projectRoot) {
40
+ const pkgPath = path.join(projectRoot, 'package.json');
41
+ if (fs.existsSync(pkgPath)) {
42
+ try {
43
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
44
+ if (typeof pkg.name === 'string' && pkg.name)
45
+ return pkg.name;
46
+ }
47
+ catch {
48
+ // fall through
49
+ }
50
+ }
51
+ return 'my-cosmolo-site';
52
+ }
53
+ function injectPackageScripts(projectRoot, adapter) {
40
54
  const pkgPath = path.join(projectRoot, 'package.json');
41
55
  if (!fs.existsSync(pkgPath))
42
56
  return;
@@ -49,6 +63,9 @@ function injectPackageScripts(projectRoot) {
49
63
  'generate:page': 'cosmolo generate page',
50
64
  'generate:category': 'cosmolo generate category',
51
65
  };
66
+ if (adapter === 'cloudflare') {
67
+ scripts['deploy'] = 'bun run build && bunx wrangler pages deploy .svelte-kit/cloudflare';
68
+ }
52
69
  for (const [key, val] of Object.entries(scripts)) {
53
70
  if (!pkg.scripts[key]) {
54
71
  pkg.scripts[key] = val;
@@ -64,6 +81,40 @@ function injectPackageScripts(projectRoot) {
64
81
  console.log(' updated package.json (added cosmolo dependency + generate:* scripts)');
65
82
  }
66
83
  }
84
+ function svelteConfigContent(adapter) {
85
+ const pkg = adapter === 'ssg' ? '@sveltejs/adapter-static' : '@sveltejs/adapter-cloudflare';
86
+ return (`import adapter from '${pkg}';\n` +
87
+ `import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';\n\n` +
88
+ `/** @type {import('@sveltejs/kit').Config} */\n` +
89
+ `const config = {\n` +
90
+ `\tpreprocess: vitePreprocess(),\n` +
91
+ `\tkit: {\n` +
92
+ `\t\tadapter: adapter(),\n` +
93
+ `\t},\n` +
94
+ `};\n\n` +
95
+ `export default config;\n`);
96
+ }
97
+ function githubActionsContent(projectName) {
98
+ return (`name: Deploy to Cloudflare Pages\n\n` +
99
+ `on:\n` +
100
+ ` push:\n` +
101
+ ` branches: [main]\n\n` +
102
+ `jobs:\n` +
103
+ ` deploy:\n` +
104
+ ` runs-on: ubuntu-latest\n` +
105
+ ` permissions:\n` +
106
+ ` contents: read\n` +
107
+ ` deployments: write\n` +
108
+ ` steps:\n` +
109
+ ` - uses: actions/checkout@v4\n` +
110
+ ` - uses: oven-sh/setup-bun@v2\n` +
111
+ ` - run: bun install --frozen-lockfile\n` +
112
+ ` - run: bun run build\n` +
113
+ ` - uses: cloudflare/wrangler-action@v3\n` +
114
+ ` with:\n` +
115
+ ` apiToken: \${{ secrets.CF_API_TOKEN }}\n` +
116
+ ` command: pages deploy .svelte-kit/cloudflare --project-name=${projectName}\n`);
117
+ }
67
118
  // ─── main ─────────────────────────────────────────────────────────────────────
68
119
  export async function main() {
69
120
  const PROJECT_ROOT = process.cwd();
@@ -82,15 +133,16 @@ export async function main() {
82
133
  const mode = modeRaw === '1' ? 'full' : 'slim';
83
134
  // ── Adapter selection ───────────────────────────────────────────────────
84
135
  console.log('\nChoose your deployment adapter:\n');
85
- console.log(' 1) SSG — @sveltejs/adapter-static (Cloudflare Pages static, GitHub Pages, etc.)');
86
- console.log(' 2) Serverless/SSR Cloudflare Workers, Vercel, Node, etc.\n');
136
+ console.log(' 1) SSG — @sveltejs/adapter-static (GitHub Pages, Cloudflare Pages static, etc.)');
137
+ console.log(' 2) Cloudflare @sveltejs/adapter-cloudflare (Workers / Pages SSR)');
138
+ console.log(' 3) Serverless — Vercel, Node, etc.\n');
87
139
  let adapterRaw = '';
88
- while (!['1', '2'].includes(adapterRaw)) {
89
- adapterRaw = (await ask(rl, 'Adapter [1/2]: ')).trim();
90
- if (!['1', '2'].includes(adapterRaw))
91
- console.log(' Please enter 1 or 2.');
140
+ while (!['1', '2', '3'].includes(adapterRaw)) {
141
+ adapterRaw = (await ask(rl, 'Adapter [1/2/3]: ')).trim();
142
+ if (!['1', '2', '3'].includes(adapterRaw))
143
+ console.log(' Please enter 1, 2, or 3.');
92
144
  }
93
- const isSSG = adapterRaw === '1';
145
+ const adapter = adapterRaw === '1' ? 'ssg' : adapterRaw === '2' ? 'cloudflare' : 'serverless';
94
146
  // ── Collect files ───────────────────────────────────────────────────────
95
147
  const sharedFiles = collectFiles(path.join(TEMPLATE_DIR, 'shared'));
96
148
  const fullFiles = mode === 'full' ? collectFiles(path.join(TEMPLATE_DIR, 'full')) : [];
@@ -100,6 +152,16 @@ export async function main() {
100
152
  ];
101
153
  const layoutTsPath = path.join(PROJECT_ROOT, 'src/routes/+layout.ts');
102
154
  const layoutTsContent = 'export const prerender = true;\n';
155
+ const wranglerTomlPath = path.join(PROJECT_ROOT, 'wrangler.toml');
156
+ const appDtsPath = path.join(PROJECT_ROOT, 'src/app.d.ts');
157
+ const svelteConfigPath = path.join(PROJECT_ROOT, 'svelte.config.js');
158
+ const ghaWorkflowPath = path.join(PROJECT_ROOT, '.github', 'workflows', 'deploy.yml');
159
+ // ── GitHub Actions prompt (Cloudflare only, before conflict detection) ──
160
+ let generateGha = false;
161
+ if (adapter === 'cloudflare') {
162
+ const ans = (await ask(rl, '\nGenerate GitHub Actions deploy workflow? [y/N]: ')).toLowerCase();
163
+ generateGha = ans === 'y';
164
+ }
103
165
  // ── Conflict detection ──────────────────────────────────────────────────
104
166
  const conflicts = [];
105
167
  for (const [, rel] of allFiles) {
@@ -107,8 +169,21 @@ export async function main() {
107
169
  if (fs.existsSync(dest))
108
170
  conflicts.push(path.relative(PROJECT_ROOT, dest));
109
171
  }
110
- if (isSSG && fs.existsSync(layoutTsPath)) {
111
- conflicts.push(path.relative(PROJECT_ROOT, layoutTsPath));
172
+ if (adapter === 'ssg') {
173
+ if (fs.existsSync(layoutTsPath))
174
+ conflicts.push(path.relative(PROJECT_ROOT, layoutTsPath));
175
+ if (fs.existsSync(svelteConfigPath))
176
+ conflicts.push('svelte.config.js');
177
+ }
178
+ if (adapter === 'cloudflare') {
179
+ if (fs.existsSync(wranglerTomlPath))
180
+ conflicts.push('wrangler.toml');
181
+ if (fs.existsSync(appDtsPath))
182
+ conflicts.push('src/app.d.ts');
183
+ if (fs.existsSync(svelteConfigPath))
184
+ conflicts.push('svelte.config.js');
185
+ if (generateGha && fs.existsSync(ghaWorkflowPath))
186
+ conflicts.push('.github/workflows/deploy.yml');
112
187
  }
113
188
  if (conflicts.length > 0) {
114
189
  console.log('\nThe following files already exist:\n');
@@ -133,27 +208,104 @@ export async function main() {
133
208
  copyFile(src, dest);
134
209
  console.log(` created ${path.relative(PROJECT_ROOT, dest)}`);
135
210
  }
136
- if (isSSG) {
211
+ if (adapter === 'ssg') {
137
212
  writeFile(layoutTsPath, layoutTsContent);
138
213
  console.log(` created src/routes/+layout.ts`);
214
+ writeFile(svelteConfigPath, svelteConfigContent('ssg'));
215
+ console.log(` created svelte.config.js`);
139
216
  }
140
- injectPackageScripts(PROJECT_ROOT);
217
+ if (adapter === 'cloudflare') {
218
+ writeFile(svelteConfigPath, svelteConfigContent('cloudflare'));
219
+ console.log(` created svelte.config.js`);
220
+ const projectName = readProjectName(PROJECT_ROOT);
221
+ const wranglerToml = [
222
+ `name = "${projectName}"`,
223
+ `compatibility_date = "${new Date().toISOString().slice(0, 10)}"`,
224
+ `compatibility_flags = ["nodejs_compat"]`,
225
+ ``,
226
+ `# Uncomment to add Cloudflare D1 (run: bunx cosmolo migrate:db)`,
227
+ `# [[d1_databases]]`,
228
+ `# binding = "DB"`,
229
+ `# database_name = "${projectName}-db"`,
230
+ `# database_id = "" # fill in after: bunx wrangler d1 create ${projectName}-db`,
231
+ ].join('\n') + '\n';
232
+ writeFile(wranglerTomlPath, wranglerToml);
233
+ console.log(` created wrangler.toml`);
234
+ const appDts = [
235
+ `// See https://svelte.dev/docs/kit/types#app.d.ts`,
236
+ `// Install @cloudflare/workers-types for full type support:`,
237
+ `// bun add -D @cloudflare/workers-types`,
238
+ `declare global {`,
239
+ `\tnamespace App {`,
240
+ `\t\tinterface Platform {`,
241
+ `\t\t\tenv: Env;`,
242
+ `\t\t\tcf: CfProperties;`,
243
+ `\t\t\tctx: ExecutionContext;`,
244
+ `\t\t}`,
245
+ `\t}`,
246
+ `}`,
247
+ ``,
248
+ `export {};`,
249
+ ].join('\n') + '\n';
250
+ writeFile(appDtsPath, appDts);
251
+ console.log(` created src/app.d.ts`);
252
+ if (generateGha) {
253
+ writeFile(ghaWorkflowPath, githubActionsContent(projectName));
254
+ console.log(` created .github/workflows/deploy.yml`);
255
+ }
256
+ }
257
+ injectPackageScripts(PROJECT_ROOT, adapter);
141
258
  // ── Next steps ──────────────────────────────────────────────────────────
142
259
  console.log('\nDone! Next steps:\n');
143
260
  console.log(' 1. Run: bun install');
144
- if (isSSG) {
261
+ if (adapter === 'ssg') {
145
262
  console.log(' 2. Install adapter: bun add -D @sveltejs/adapter-static');
263
+ if (mode === 'full') {
264
+ console.log(' 3. Install sass: bun add -D sass (SCSS used in Svelte templates)');
265
+ console.log(' 4. Run: bun dev');
266
+ }
267
+ else {
268
+ console.log(' 3. Add your own +page.svelte files for each route.');
269
+ console.log(' 4. Run: bun dev');
270
+ }
146
271
  }
147
- else {
148
- console.log(' 2. Install adapter: bun add -D @sveltejs/adapter-cloudflare (or your adapter)');
149
- }
150
- if (mode === 'full') {
151
- console.log(' 3. Install sass: bun add -D sass (SCSS used in Svelte templates)');
152
- console.log(' 4. Run: bun dev');
272
+ else if (adapter === 'cloudflare') {
273
+ console.log(' 2. Install adapter: bun add -D @sveltejs/adapter-cloudflare');
274
+ console.log(' 3. Install types: bun add -D @cloudflare/workers-types');
275
+ if (generateGha) {
276
+ console.log(' 4. Add secret to GitHub repo: CF_API_TOKEN (Cloudflare API token)');
277
+ if (mode === 'full') {
278
+ console.log(' 5. Install sass: bun add -D sass (SCSS used in Svelte templates)');
279
+ console.log(' 6. Push to main — GitHub Actions will build and deploy automatically.');
280
+ }
281
+ else {
282
+ console.log(' 5. Add your own +page.svelte files for each route.');
283
+ console.log(' 6. Push to main — GitHub Actions will build and deploy automatically.');
284
+ }
285
+ }
286
+ else {
287
+ if (mode === 'full') {
288
+ console.log(' 4. Install sass: bun add -D sass (SCSS used in Svelte templates)');
289
+ console.log(' 5. Run: bunx wrangler dev (or bun dev for local Vite)');
290
+ console.log(' 6. Deploy: bun run deploy');
291
+ }
292
+ else {
293
+ console.log(' 4. Add your own +page.svelte files for each route.');
294
+ console.log(' 5. Run: bunx wrangler dev (or bun dev for local Vite)');
295
+ console.log(' 6. Deploy: bun run deploy');
296
+ }
297
+ }
153
298
  }
154
299
  else {
155
- console.log(' 3. Add your own +page.svelte files for each route.');
156
- console.log(' 4. Run: bun dev');
300
+ console.log(' 2. Install adapter: bun add -D @sveltejs/adapter-vercel (or your adapter)');
301
+ if (mode === 'full') {
302
+ console.log(' 3. Install sass: bun add -D sass (SCSS used in Svelte templates)');
303
+ console.log(' 4. Run: bun dev');
304
+ }
305
+ else {
306
+ console.log(' 3. Add your own +page.svelte files for each route.');
307
+ console.log(' 4. Run: bun dev');
308
+ }
157
309
  }
158
310
  console.log('\n See https://github.com/alcogy/cosmolo for full documentation.\n');
159
311
  }
@@ -0,0 +1,2 @@
1
+ import type { ResolvedCosmoloConfig } from '../../types.js';
2
+ export declare function drizzleSetup(_config: ResolvedCosmoloConfig): Promise<void>;