@stravigor/create 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stravigor/create",
3
- "version": "0.4.9",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Scaffold a new Strav application",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -2,10 +2,10 @@
2
2
  import { existsSync } from 'node:fs'
3
3
  import { join, resolve } from 'node:path'
4
4
  import { select, input } from './prompts.ts'
5
- import { scaffold } from './scaffold.ts'
6
- import type { ScaffoldOptions } from './templates/shared.ts'
5
+ import { scaffold, type ScaffoldOptions } from './scaffold.ts'
6
+ import pkg from '../package.json'
7
7
 
8
- const VERSION = '0.4.8'
8
+ const VERSION = pkg.version
9
9
 
10
10
  // ── Colors ──────────────────────────────────────────────────────────
11
11
 
@@ -47,7 +47,7 @@ function parseArgs(): ParsedArgs {
47
47
  }
48
48
  } else if (arg === '--db') {
49
49
  result.db = args[++i]
50
- } else if (!arg.startsWith('-') && !result.projectName) {
50
+ } else if (arg && !arg.startsWith('-') && !result.projectName) {
51
51
  result.projectName = arg
52
52
  }
53
53
  }
package/src/scaffold.ts CHANGED
@@ -1,20 +1,54 @@
1
- import { mkdirSync } from 'node:fs'
1
+ import { readdirSync, mkdirSync, statSync } from 'node:fs'
2
2
  import { join, dirname } from 'node:path'
3
- import { getSharedFiles, type ScaffoldOptions } from './templates/shared.ts'
4
- import { getApiFiles } from './templates/api.ts'
5
- import { getWebFiles } from './templates/web.ts'
3
+ import pkg from '../package.json'
4
+
5
+ export interface ScaffoldOptions {
6
+ projectName: string
7
+ template: 'api' | 'web'
8
+ dbName: string
9
+ }
6
10
 
7
11
  export async function scaffold(root: string, opts: ScaffoldOptions): Promise<void> {
8
- // Collect all files
9
- const files = [
10
- ...getSharedFiles(opts),
11
- ...(opts.template === 'web' ? getWebFiles(opts) : getApiFiles(opts)),
12
- ]
13
-
14
- // Create directories and write files
15
- for (const file of files) {
16
- const fullPath = join(root, file.path)
17
- mkdirSync(dirname(fullPath), { recursive: true })
18
- await Bun.write(fullPath, file.content)
12
+ const templatesDir = join(import.meta.dir, 'templates')
13
+ const appKey = crypto.randomUUID()
14
+
15
+ const replacements: Record<string, string> = {
16
+ __PROJECT_NAME__: opts.projectName,
17
+ __DB_NAME__: opts.dbName,
18
+ __APP_KEY__: appKey,
19
+ __CORE_VERSION__: `^${pkg.version}`,
20
+ }
21
+
22
+ // Copy shared files first, then template-specific (may override shared)
23
+ await copyDir(join(templatesDir, 'shared'), root, replacements)
24
+ await copyDir(join(templatesDir, opts.template), root, replacements)
25
+ }
26
+
27
+ async function copyDir(
28
+ srcDir: string,
29
+ destDir: string,
30
+ replacements: Record<string, string>
31
+ ): Promise<void> {
32
+ const entries = readdirSync(srcDir)
33
+
34
+ for (const entry of entries) {
35
+ const srcPath = join(srcDir, entry)
36
+ const destPath = join(destDir, entry.replace(/\.tpl$/, ''))
37
+
38
+ if (statSync(srcPath).isDirectory()) {
39
+ await copyDir(srcPath, destPath, replacements)
40
+ } else {
41
+ mkdirSync(dirname(destPath), { recursive: true })
42
+ const content = await Bun.file(srcPath).text()
43
+ await Bun.write(destPath, applyReplacements(content, replacements))
44
+ }
45
+ }
46
+ }
47
+
48
+ function applyReplacements(content: string, replacements: Record<string, string>): string {
49
+ let result = content
50
+ for (const [placeholder, value] of Object.entries(replacements)) {
51
+ result = result.replaceAll(placeholder, value)
19
52
  }
53
+ return result
20
54
  }
@@ -0,0 +1,7 @@
1
+ import { env } from '@stravigor/core/helpers/env'
2
+
3
+ export default {
4
+ host: env('HOST', '0.0.0.0'),
5
+ port: env.int('PORT', 3000),
6
+ domain: env('DOMAIN', 'localhost'),
7
+ }
@@ -0,0 +1,29 @@
1
+ import 'reflect-metadata'
2
+ import { app } from '@stravigor/core/core'
3
+ import { router } from '@stravigor/core/http'
4
+ import { ConfigProvider, DatabaseProvider, EncryptionProvider } from '@stravigor/core/providers'
5
+ import BaseModel from '@stravigor/core/orm/base_model'
6
+ import Database from '@stravigor/core/database/database'
7
+ import Server from '@stravigor/core/http/server'
8
+ import { ExceptionHandler } from '@stravigor/core/exceptions'
9
+
10
+ // Register service providers
11
+ app.use(new ConfigProvider()).use(new DatabaseProvider()).use(new EncryptionProvider())
12
+
13
+ // Boot services (loads config, connects database, derives encryption keys)
14
+ await app.start()
15
+
16
+ // Initialize ORM
17
+ new BaseModel(app.resolve(Database))
18
+
19
+ // Configure router
20
+ router.useExceptionHandler(new ExceptionHandler(true))
21
+ router.cors()
22
+
23
+ // Load routes
24
+ await import('./start/routes')
25
+
26
+ // Start HTTP server
27
+ app.singleton(Server)
28
+ const server = app.resolve(Server)
29
+ server.start(router)
@@ -0,0 +1,12 @@
1
+ import { router } from '@stravigor/core/http'
2
+
3
+ router.get('/', () => {
4
+ return Response.json({
5
+ name: '__PROJECT_NAME__',
6
+ status: 'running',
7
+ })
8
+ })
9
+
10
+ router.get('/health', () => {
11
+ return Response.json({ status: 'ok' })
12
+ })
@@ -0,0 +1,12 @@
1
+ import { test, expect } from 'bun:test'
2
+ import { TestCase } from '@stravigor/testing'
3
+
4
+ const t = await TestCase.boot({
5
+ routes: () => import('../start/routes'),
6
+ })
7
+
8
+ test('health check returns ok', async () => {
9
+ const res = await t.get('/health')
10
+ expect(res.status).toBe(200)
11
+ expect(await res.json()).toEqual({ status: 'ok' })
12
+ })
@@ -0,0 +1,13 @@
1
+ APP_ENV=local
2
+ APP_DEBUG=true
3
+ APP_KEY=__APP_KEY__
4
+
5
+ HOST=0.0.0.0
6
+ PORT=3000
7
+ DOMAIN=localhost
8
+
9
+ DB_HOST=127.0.0.1
10
+ DB_PORT=5432
11
+ DB_USERNAME=postgres
12
+ DB_PASSWORD=
13
+ DB_DATABASE=__DB_NAME__
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ .env
3
+ app/
4
+ database/migrations/
5
+ *.log
File without changes
File without changes
@@ -0,0 +1,7 @@
1
+ import { env } from '@stravigor/core/helpers/env'
2
+
3
+ export default {
4
+ env: env('APP_ENV', 'local'),
5
+ debug: env.bool('APP_DEBUG', true),
6
+ key: env('APP_KEY'),
7
+ }
@@ -0,0 +1,9 @@
1
+ import { env } from '@stravigor/core/helpers/env'
2
+
3
+ export default {
4
+ host: env('DB_HOST', '127.0.0.1'),
5
+ port: env.int('DB_PORT', 5432),
6
+ username: env('DB_USERNAME', 'postgres'),
7
+ password: env('DB_PASSWORD', ''),
8
+ database: env('DB_DATABASE', '__DB_NAME__'),
9
+ }
@@ -0,0 +1,6 @@
1
+ import { env } from '@stravigor/core/helpers/env'
2
+
3
+ export default {
4
+ key: env('APP_KEY'),
5
+ previousKeys: [],
6
+ }
@@ -0,0 +1,10 @@
1
+ import { defineSchema, t, Archetype } from '@stravigor/core/schema'
2
+
3
+ export default defineSchema('user', {
4
+ archetype: Archetype.Entity,
5
+ fields: {
6
+ email: t.varchar(255).required().unique().index(),
7
+ name: t.varchar(255).required(),
8
+ password: t.varchar(255).required().sensitive(),
9
+ },
10
+ })
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "bun --hot index.ts",
8
+ "start": "bun index.ts",
9
+ "test": "bun test tests/"
10
+ },
11
+ "dependencies": {
12
+ "@stravigor/core": "__CORE_VERSION__",
13
+ "luxon": "^3.7.2",
14
+ "reflect-metadata": "^0.2.2"
15
+ },
16
+ "devDependencies": {
17
+ "@types/bun": "latest",
18
+ "@types/luxon": "^3.7.1",
19
+ "@stravigor/testing": "__CORE_VERSION__"
20
+ }
21
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import '@stravigor/core/cli/strav'
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [
4
+ "ESNext"
5
+ ],
6
+ "target": "ESNext",
7
+ "module": "ESNext",
8
+ "moduleDetection": "force",
9
+ "allowJs": true,
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "noEmit": true,
13
+ "experimentalDecorators": true,
14
+ "emitDecoratorMetadata": true,
15
+ "strict": true,
16
+ "skipLibCheck": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noUnusedLocals": false,
19
+ "noUnusedParameters": false
20
+ },
21
+ "include": [
22
+ "**/*.ts"
23
+ ]
24
+ }
@@ -0,0 +1,8 @@
1
+ import { env } from '@stravigor/core/helpers/env'
2
+
3
+ export default {
4
+ host: env('HOST', '0.0.0.0'),
5
+ port: env.int('PORT', 3000),
6
+ domain: env('DOMAIN', 'localhost'),
7
+ public: './public',
8
+ }
@@ -0,0 +1,7 @@
1
+ export default {
2
+ cookie: 'strav_session',
3
+ lifetime: 120,
4
+ httpOnly: true,
5
+ secure: false,
6
+ sameSite: 'lax' as const,
7
+ }
@@ -0,0 +1,4 @@
1
+ export default {
2
+ directory: 'views',
3
+ cache: false,
4
+ }
@@ -0,0 +1,39 @@
1
+ import 'reflect-metadata'
2
+ import { app } from '@stravigor/core/core'
3
+ import { router } from '@stravigor/core/http'
4
+ import {
5
+ ConfigProvider,
6
+ DatabaseProvider,
7
+ EncryptionProvider,
8
+ SessionProvider,
9
+ ViewProvider,
10
+ } from '@stravigor/core/providers'
11
+ import BaseModel from '@stravigor/core/orm/base_model'
12
+ import Database from '@stravigor/core/database/database'
13
+ import Server from '@stravigor/core/http/server'
14
+ import { ExceptionHandler } from '@stravigor/core/exceptions'
15
+
16
+ // Register service providers
17
+ app
18
+ .use(new ConfigProvider())
19
+ .use(new DatabaseProvider())
20
+ .use(new EncryptionProvider())
21
+ .use(new SessionProvider())
22
+ .use(new ViewProvider())
23
+
24
+ // Boot services (loads config, connects database, derives encryption keys, starts sessions)
25
+ await app.start()
26
+
27
+ // Initialize ORM
28
+ new BaseModel(app.resolve(Database))
29
+
30
+ // Configure router
31
+ router.useExceptionHandler(new ExceptionHandler(true))
32
+
33
+ // Load routes
34
+ await import('./start/routes')
35
+
36
+ // Start HTTP server
37
+ app.singleton(Server)
38
+ const server = app.resolve(Server)
39
+ server.start(router)
@@ -0,0 +1,53 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: 'Barlow Semi Condensed', sans-serif;
9
+ background: #f8fafc;
10
+ color: #1e293b;
11
+ min-height: 100vh;
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ }
16
+
17
+ .container {
18
+ text-align: center;
19
+ padding: 2rem;
20
+ }
21
+
22
+ h1 {
23
+ font-size: 2.5rem;
24
+ font-weight: 700;
25
+ margin-bottom: 1rem;
26
+ color: oklch(57.7% 0.245 27.325);
27
+ }
28
+
29
+ p {
30
+ font-size: 1.125rem;
31
+ color: #64748b;
32
+ margin-bottom: 2rem;
33
+ }
34
+
35
+ p strong {
36
+ color: #1e293b;
37
+ }
38
+
39
+ .links a {
40
+ color: #2563eb;
41
+ text-decoration: none;
42
+ font-size: 0.875rem;
43
+ font-weight: 600;
44
+ border: 1px solid #e2e8f0;
45
+ padding: 0.5rem 1rem;
46
+ border-radius: 0.375rem;
47
+ transition: all 0.2s;
48
+ }
49
+
50
+ .links a:hover {
51
+ background: #eff6ff;
52
+ border-color: #2563eb;
53
+ }
@@ -0,0 +1,10 @@
1
+ import { router } from '@stravigor/core/http'
2
+ import { view } from '@stravigor/core/view'
3
+
4
+ router.get('/', async () => {
5
+ return view('welcome', { name: '__PROJECT_NAME__' })
6
+ })
7
+
8
+ router.get('/api/health', () => {
9
+ return Response.json({ status: 'ok' })
10
+ })
@@ -0,0 +1,12 @@
1
+ import { test, expect } from 'bun:test'
2
+ import { TestCase } from '@stravigor/testing'
3
+
4
+ const t = await TestCase.boot({
5
+ routes: () => import('../start/routes'),
6
+ })
7
+
8
+ test('health check returns ok', async () => {
9
+ const res = await t.get('/api/health')
10
+ expect(res.status).toBe(200)
11
+ expect(await res.json()).toEqual({ status: 'ok' })
12
+ })
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{ name }} — Strav</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Barlow+Semi+Condensed:wght@400;600;700&display=swap" rel="stylesheet">
10
+ <link rel="stylesheet" href="/styles.css">
11
+ </head>
12
+ <body>
13
+ <div class="container">
14
+ <h1>Welcome to Strav</h1>
15
+ <p>Your application <strong>{{ name }}</strong> is up and running.</p>
16
+ <div class="links">
17
+ <a href="https://github.com/nicoyambura/stravigor" target="_blank">Documentation</a>
18
+ </div>
19
+ </div>
20
+ </body>
21
+ </html>
@@ -1,74 +0,0 @@
1
- import type { TemplateFile, ScaffoldOptions } from './shared.ts'
2
-
3
- export function getApiFiles(opts: ScaffoldOptions): TemplateFile[] {
4
- return [
5
- { path: 'index.ts', content: indexTs(opts) },
6
- { path: 'config/http.ts', content: configHttp() },
7
- { path: 'start/routes.ts', content: routes(opts) },
8
- ]
9
- }
10
-
11
- function indexTs(opts: ScaffoldOptions): string {
12
- return `import 'reflect-metadata'
13
- import { app } from '@stravigor/core/core'
14
- import { router } from '@stravigor/core/http'
15
- import {
16
- ConfigProvider, DatabaseProvider, EncryptionProvider,
17
- } from '@stravigor/core/providers'
18
- import BaseModel from '@stravigor/core/orm/base_model'
19
- import Database from '@stravigor/core/database/database'
20
- import Server from '@stravigor/core/http/server'
21
- import { ExceptionHandler } from '@stravigor/core/exceptions'
22
-
23
- // Register service providers
24
- app
25
- .use(new ConfigProvider())
26
- .use(new DatabaseProvider())
27
- .use(new EncryptionProvider())
28
-
29
- // Boot services (loads config, connects database, derives encryption keys)
30
- await app.start()
31
-
32
- // Initialize ORM
33
- new BaseModel(app.resolve(Database))
34
-
35
- // Configure router
36
- router.useExceptionHandler(new ExceptionHandler(true))
37
- router.cors()
38
-
39
- // Load routes
40
- await import('./start/routes')
41
-
42
- // Start HTTP server
43
- app.singleton(Server)
44
- const server = app.resolve(Server)
45
- server.start(router)
46
- `
47
- }
48
-
49
- function configHttp(): string {
50
- return `import { env } from '@stravigor/core/helpers/env'
51
-
52
- export default {
53
- host: env('HOST', '0.0.0.0'),
54
- port: env.int('PORT', 3000),
55
- domain: env('DOMAIN', 'localhost'),
56
- }
57
- `
58
- }
59
-
60
- function routes(opts: ScaffoldOptions): string {
61
- return `import { router } from '@stravigor/core/http'
62
-
63
- router.get('/', () => {
64
- return Response.json({
65
- name: '${opts.projectName}',
66
- status: 'running',
67
- })
68
- })
69
-
70
- router.get('/health', () => {
71
- return Response.json({ status: 'ok' })
72
- })
73
- `
74
- }
@@ -1,164 +0,0 @@
1
- export interface TemplateFile {
2
- path: string
3
- content: string
4
- }
5
-
6
- export interface ScaffoldOptions {
7
- projectName: string
8
- template: 'api' | 'web'
9
- dbName: string
10
- }
11
-
12
- export function getSharedFiles(opts: ScaffoldOptions): TemplateFile[] {
13
- const appKey = crypto.randomUUID()
14
-
15
- return [
16
- { path: 'package.json', content: packageJson(opts) },
17
- { path: 'tsconfig.json', content: tsconfig() },
18
- { path: '.env', content: dotEnv(opts, appKey) },
19
- { path: '.gitignore', content: gitignore() },
20
- { path: 'strav.ts', content: stravTs() },
21
- { path: 'config/app.ts', content: configApp() },
22
- { path: 'config/database.ts', content: configDatabase(opts) },
23
- { path: 'config/encryption.ts', content: configEncryption() },
24
- { path: 'database/schemas/.gitkeep', content: '' },
25
- { path: 'tests/health.test.ts', content: healthTest(opts) },
26
- ]
27
- }
28
-
29
- function packageJson(opts: ScaffoldOptions): string {
30
- return (
31
- JSON.stringify(
32
- {
33
- name: opts.projectName,
34
- version: '0.0.1',
35
- type: 'module',
36
- private: true,
37
- scripts: {
38
- dev: 'bun --hot index.ts',
39
- start: 'bun index.ts',
40
- test: 'bun test tests/',
41
- },
42
- dependencies: {
43
- '@stravigor/core': '^0.4.0',
44
- luxon: '^3.7.2',
45
- 'reflect-metadata': '^0.2.2',
46
- },
47
- devDependencies: {
48
- '@types/bun': 'latest',
49
- '@types/luxon': '^3.7.1',
50
- '@stravigor/testing': '^0.4.0',
51
- },
52
- },
53
- null,
54
- 2
55
- ) + '\n'
56
- )
57
- }
58
-
59
- function tsconfig(): string {
60
- return (
61
- JSON.stringify(
62
- {
63
- compilerOptions: {
64
- lib: ['ESNext'],
65
- target: 'ESNext',
66
- module: 'ESNext',
67
- moduleDetection: 'force',
68
- allowJs: true,
69
- moduleResolution: 'bundler',
70
- allowImportingTsExtensions: true,
71
- noEmit: true,
72
- experimentalDecorators: true,
73
- emitDecoratorMetadata: true,
74
- strict: true,
75
- skipLibCheck: true,
76
- noFallthroughCasesInSwitch: true,
77
- noUnusedLocals: false,
78
- noUnusedParameters: false,
79
- },
80
- include: ['**/*.ts'],
81
- },
82
- null,
83
- 2
84
- ) + '\n'
85
- )
86
- }
87
-
88
- function dotEnv(opts: ScaffoldOptions, appKey: string): string {
89
- return `APP_ENV=local
90
- APP_DEBUG=true
91
- APP_KEY=${appKey}
92
-
93
- HOST=0.0.0.0
94
- PORT=3000
95
- DOMAIN=localhost
96
-
97
- DB_HOST=127.0.0.1
98
- DB_PORT=5432
99
- DB_USERNAME=postgres
100
- DB_PASSWORD=
101
- DB_DATABASE=${opts.dbName}
102
- `
103
- }
104
-
105
- function gitignore(): string {
106
- return `node_modules/
107
- .env
108
- app/
109
- database/migrations/
110
- *.log
111
- `
112
- }
113
-
114
- function stravTs(): string {
115
- return `#!/usr/bin/env bun
116
- import '@stravigor/core/cli/strav'
117
- `
118
- }
119
-
120
- function configApp(): string {
121
- return `import { env } from '@stravigor/core/helpers/env'
122
-
123
- export default {
124
- env: env('APP_ENV', 'local'),
125
- debug: env.bool('APP_DEBUG', true),
126
- key: env('APP_KEY'),
127
- }
128
- `
129
- }
130
-
131
- function configDatabase(opts: ScaffoldOptions): string {
132
- return `import { env } from '@stravigor/core/helpers/env'
133
-
134
- export default {
135
- host: env('DB_HOST', '127.0.0.1'),
136
- port: env.int('DB_PORT', 5432),
137
- username: env('DB_USERNAME', 'postgres'),
138
- password: env('DB_PASSWORD', ''),
139
- database: env('DB_DATABASE', '${opts.dbName}'),
140
- }
141
- `
142
- }
143
-
144
- function configEncryption(): string {
145
- return `import { env } from '@stravigor/core/helpers/env'
146
-
147
- export default {
148
- key: env('APP_KEY'),
149
- previousKeys: [],
150
- }
151
- `
152
- }
153
-
154
- function healthTest(opts: ScaffoldOptions): string {
155
- const path = opts.template === 'web' ? '/api/health' : '/health'
156
- return `import { test, expect } from 'bun:test'
157
-
158
- test('health check returns ok', async () => {
159
- const res = await fetch('http://localhost:3000${path}')
160
- const json = await res.json()
161
- expect(json.status).toBe('ok')
162
- })
163
- `
164
- }
@@ -1,179 +0,0 @@
1
- import type { TemplateFile, ScaffoldOptions } from './shared.ts'
2
-
3
- export function getWebFiles(opts: ScaffoldOptions): TemplateFile[] {
4
- return [
5
- { path: 'index.ts', content: indexTs(opts) },
6
- { path: 'config/http.ts', content: configHttp() },
7
- { path: 'config/session.ts', content: configSession() },
8
- { path: 'config/view.ts', content: configView() },
9
- { path: 'start/routes.ts', content: routes(opts) },
10
- { path: 'views/welcome.strav', content: welcomeView() },
11
- { path: 'public/styles.css', content: stylesCss() },
12
- ]
13
- }
14
-
15
- function indexTs(opts: ScaffoldOptions): string {
16
- return `import 'reflect-metadata'
17
- import { app } from '@stravigor/core/core'
18
- import { router } from '@stravigor/core/http'
19
- import {
20
- ConfigProvider, DatabaseProvider, EncryptionProvider, SessionProvider, ViewProvider,
21
- } from '@stravigor/core/providers'
22
- import BaseModel from '@stravigor/core/orm/base_model'
23
- import Database from '@stravigor/core/database/database'
24
- import Server from '@stravigor/core/http/server'
25
- import { ExceptionHandler } from '@stravigor/core/exceptions'
26
-
27
- // Register service providers
28
- app
29
- .use(new ConfigProvider())
30
- .use(new DatabaseProvider())
31
- .use(new EncryptionProvider())
32
- .use(new SessionProvider())
33
- .use(new ViewProvider())
34
-
35
- // Boot services (loads config, connects database, derives encryption keys, starts sessions)
36
- await app.start()
37
-
38
- // Initialize ORM
39
- new BaseModel(app.resolve(Database))
40
-
41
- // Configure router
42
- router.useExceptionHandler(new ExceptionHandler(true))
43
-
44
- // Load routes
45
- await import('./start/routes')
46
-
47
- // Start HTTP server
48
- app.singleton(Server)
49
- const server = app.resolve(Server)
50
- server.start(router)
51
- `
52
- }
53
-
54
- function configHttp(): string {
55
- return `import { env } from '@stravigor/core/helpers/env'
56
-
57
- export default {
58
- host: env('HOST', '0.0.0.0'),
59
- port: env.int('PORT', 3000),
60
- domain: env('DOMAIN', 'localhost'),
61
- public: './public',
62
- }
63
- `
64
- }
65
-
66
- function configSession(): string {
67
- return `export default {
68
- cookie: 'strav_session',
69
- lifetime: 120,
70
- httpOnly: true,
71
- secure: false,
72
- sameSite: 'lax' as const,
73
- }
74
- `
75
- }
76
-
77
- function configView(): string {
78
- return `export default {
79
- directory: 'views',
80
- cache: false,
81
- }
82
- `
83
- }
84
-
85
- function routes(opts: ScaffoldOptions): string {
86
- return `import { router } from '@stravigor/core/http'
87
- import { view } from '@stravigor/core/view'
88
-
89
- router.get('/', async () => {
90
- return view('welcome', { name: '${opts.projectName}' })
91
- })
92
-
93
- router.get('/api/health', () => {
94
- return Response.json({ status: 'ok' })
95
- })
96
- `
97
- }
98
-
99
- function welcomeView(): string {
100
- return `<!DOCTYPE html>
101
- <html lang="en">
102
- <head>
103
- <meta charset="UTF-8">
104
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
105
- <title>{{ name }} — Strav</title>
106
- <link rel="preconnect" href="https://fonts.googleapis.com">
107
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
108
- <link href="https://fonts.googleapis.com/css2?family=Barlow+Semi+Condensed:wght@400;600;700&display=swap" rel="stylesheet">
109
- <link rel="stylesheet" href="/styles.css">
110
- </head>
111
- <body>
112
- <div class="container">
113
- <h1>Welcome to Strav</h1>
114
- <p>Your application <strong>{{ name }}</strong> is up and running.</p>
115
- <div class="links">
116
- <a href="https://github.com/nicoyambura/stravigor" target="_blank">Documentation</a>
117
- </div>
118
- </div>
119
- </body>
120
- </html>
121
- `
122
- }
123
-
124
- function stylesCss(): string {
125
- return `* {
126
- margin: 0;
127
- padding: 0;
128
- box-sizing: border-box;
129
- }
130
-
131
- body {
132
- font-family: 'Barlow Semi Condensed', sans-serif;
133
- background: #f8fafc;
134
- color: #1e293b;
135
- min-height: 100vh;
136
- display: flex;
137
- align-items: center;
138
- justify-content: center;
139
- }
140
-
141
- .container {
142
- text-align: center;
143
- padding: 2rem;
144
- }
145
-
146
- h1 {
147
- font-size: 2.5rem;
148
- font-weight: 700;
149
- margin-bottom: 1rem;
150
- color: oklch(57.7% 0.245 27.325);
151
- }
152
-
153
- p {
154
- font-size: 1.125rem;
155
- color: #64748b;
156
- margin-bottom: 2rem;
157
- }
158
-
159
- p strong {
160
- color: #1e293b;
161
- }
162
-
163
- .links a {
164
- color: #2563eb;
165
- text-decoration: none;
166
- font-size: 0.875rem;
167
- font-weight: 600;
168
- border: 1px solid #e2e8f0;
169
- padding: 0.5rem 1rem;
170
- border-radius: 0.375rem;
171
- transition: all 0.2s;
172
- }
173
-
174
- .links a:hover {
175
- background: #eff6ff;
176
- border-color: #2563eb;
177
- }
178
- `
179
- }