@strav/spring 0.4.30 → 1.0.0-alpha.28

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 (90) hide show
  1. package/README.md +11 -52
  2. package/package.json +19 -20
  3. package/src/args.ts +119 -0
  4. package/src/cli.ts +134 -0
  5. package/src/index.ts +10 -176
  6. package/src/prompts.ts +49 -127
  7. package/src/scaffold.ts +115 -36
  8. package/src/spring_error.ts +11 -0
  9. package/src/templates/shared/README.md.tt +37 -0
  10. package/src/templates/shared/_dot_env.example.tt +12 -0
  11. package/src/templates/shared/_dot_env.tt +12 -0
  12. package/src/templates/shared/_dot_gitignore +12 -0
  13. package/src/templates/shared/app/console/_dot_gitkeep +0 -0
  14. package/src/templates/shared/app/exceptions/_dot_gitkeep +0 -0
  15. package/src/templates/shared/app/http/controllers/_dot_gitkeep +0 -0
  16. package/src/templates/shared/app/http/middleware/_dot_gitkeep +0 -0
  17. package/src/templates/shared/app/http/requests/_dot_gitkeep +0 -0
  18. package/src/templates/shared/app/jobs/_dot_gitkeep +0 -0
  19. package/src/templates/shared/app/mail/_dot_gitkeep +0 -0
  20. package/src/templates/shared/app/models/_dot_gitkeep +0 -0
  21. package/src/templates/shared/app/notifications/_dot_gitkeep +0 -0
  22. package/src/templates/shared/app/policies/_dot_gitkeep +0 -0
  23. package/src/templates/shared/app/providers/app_provider.ts +18 -0
  24. package/src/templates/shared/app/repositories/_dot_gitkeep +0 -0
  25. package/src/templates/shared/bin/strav.ts +21 -0
  26. package/src/templates/shared/bootstrap/app.ts +13 -0
  27. package/src/templates/shared/bootstrap/providers.ts +29 -0
  28. package/src/templates/shared/config/app.ts.tt +13 -0
  29. package/src/templates/shared/config/http.ts +9 -0
  30. package/src/templates/shared/config/logger.ts +9 -0
  31. package/src/templates/shared/database/factories/_dot_gitkeep +0 -0
  32. package/src/templates/shared/database/migrations/_dot_gitkeep +0 -0
  33. package/src/templates/shared/database/schemas/_dot_gitkeep +0 -0
  34. package/src/templates/shared/database/seeders/_dot_gitkeep +0 -0
  35. package/src/templates/shared/package.json.tt +22 -0
  36. package/src/templates/shared/routes/api.ts +11 -0
  37. package/src/templates/shared/routes/console.ts +10 -0
  38. package/src/templates/shared/storage/cache/_dot_gitkeep +0 -0
  39. package/src/templates/shared/storage/logs/_dot_gitkeep +0 -0
  40. package/src/templates/shared/storage/uploads/_dot_gitkeep +0 -0
  41. package/src/templates/shared/tests/feature/healthz.test.ts.tt +19 -0
  42. package/src/templates/shared/tests/unit/_dot_gitkeep +0 -0
  43. package/src/templates/shared/tsconfig.json +21 -11
  44. package/src/templates/web/README.md.tt +42 -0
  45. package/src/templates/web/_dot_gitignore +13 -0
  46. package/src/templates/web/app/providers/app_provider.ts +21 -0
  47. package/src/templates/web/bootstrap/providers.ts +34 -0
  48. package/src/templates/web/config/http.ts +17 -8
  49. package/src/templates/web/config/view.ts +7 -7
  50. package/src/templates/web/package.json.tt +26 -0
  51. package/src/templates/web/public/assets/_dot_gitkeep +0 -0
  52. package/src/templates/web/resources/css/app.css +41 -0
  53. package/src/templates/web/resources/ts/islands/counter.vue +11 -0
  54. package/src/templates/web/resources/ts/islands/setup.ts +13 -0
  55. package/src/templates/web/resources/views/components/_dot_gitkeep +0 -0
  56. package/src/templates/web/resources/views/errors/404.strav +8 -0
  57. package/src/templates/web/resources/views/errors/500.strav +8 -0
  58. package/src/templates/web/resources/views/layouts/app.strav.tt +15 -0
  59. package/src/templates/web/resources/views/pages/index.strav.tt +12 -0
  60. package/src/templates/web/routes/broadcast.ts +9 -0
  61. package/src/templates/web/routes/web.ts +19 -0
  62. package/src/templates/web/tests/browser/_dot_gitkeep +0 -0
  63. package/src/version.ts +9 -0
  64. package/src/templates/api/app/controllers/controller.ts +0 -15
  65. package/src/templates/api/app/controllers/user_controller.ts +0 -69
  66. package/src/templates/api/config/database.ts +0 -9
  67. package/src/templates/api/config/http.ts +0 -17
  68. package/src/templates/api/database/factories/user_factory.ts +0 -11
  69. package/src/templates/api/database/schemas/user.ts +0 -13
  70. package/src/templates/api/database/seeders/database_seeder.ts +0 -8
  71. package/src/templates/api/database/seeders/user_seeder.ts +0 -15
  72. package/src/templates/api/index.ts +0 -11
  73. package/src/templates/api/package.json +0 -24
  74. package/src/templates/api/start/providers.ts +0 -10
  75. package/src/templates/api/start/routes.ts +0 -22
  76. package/src/templates/shared/config/app.ts +0 -7
  77. package/src/templates/shared/config/encryption.ts +0 -5
  78. package/src/templates/shared/package.json +0 -24
  79. package/src/templates/shared/storage/uploads/.gitkeep +0 -1
  80. package/src/templates/shared/strav.ts +0 -2
  81. package/src/templates/shared/tests/example.test.ts +0 -11
  82. package/src/templates/web/index.ts +0 -28
  83. package/src/templates/web/package.json +0 -26
  84. package/src/templates/web/public/builds/.gitkeep +0 -1
  85. package/src/templates/web/public/css/.gitkeep +0 -1
  86. package/src/templates/web/resources/css/app.scss +0 -77
  87. package/src/templates/web/resources/islands/counter.vue +0 -31
  88. package/src/templates/web/resources/views/layouts/app.strav +0 -18
  89. package/src/templates/web/resources/views/pages/index.strav +0 -32
  90. package/src/templates/web/start/providers.ts +0 -11
@@ -0,0 +1,19 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { HttpKernel } from '@strav/http'
3
+ import { createApp } from '../../bootstrap/app.ts'
4
+ import { providers } from '../../bootstrap/providers.ts'
5
+
6
+ describe('GET /healthz', () => {
7
+ test('returns 200 { ok: true }', async () => {
8
+ const app = createApp()
9
+ app.useProviders(providers())
10
+ await app.start()
11
+
12
+ const kernel = app.resolve(HttpKernel)
13
+ const res = await kernel.handle(new Request('http://localhost/healthz'))
14
+ expect(res.status).toBe(200)
15
+ expect(await res.json()).toEqual({ ok: true })
16
+
17
+ await app.shutdown()
18
+ })
19
+ })
File without changes
@@ -1,20 +1,30 @@
1
1
  {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
2
3
  "compilerOptions": {
3
- "strict": true,
4
- "target": "ES2022",
4
+ "target": "ESNext",
5
5
  "module": "ESNext",
6
6
  "moduleResolution": "bundler",
7
+ "lib": ["ESNext"],
8
+ "types": ["bun"],
9
+
10
+ "strict": true,
11
+ "noUncheckedIndexedAccess": true,
12
+ "noImplicitOverride": true,
13
+ "noFallthroughCasesInSwitch": true,
14
+
15
+ "experimentalDecorators": true,
16
+ "emitDecoratorMetadata": true,
17
+
7
18
  "allowImportingTsExtensions": true,
8
- "noEmit": true,
9
- "composite": false,
19
+ "verbatimModuleSyntax": true,
20
+ "isolatedModules": true,
21
+ "esModuleInterop": true,
22
+ "resolveJsonModule": true,
10
23
  "skipLibCheck": true,
11
- "allowSyntheticDefaultImports": true,
12
24
  "forceConsistentCasingInFileNames": true,
13
- "experimentalDecorators": true,
14
- "emitDecoratorMetadata": true,
15
- "useDefineForClassFields": false,
16
- "types": ["bun-types"]
25
+
26
+ "noEmit": true
17
27
  },
18
28
  "include": ["**/*.ts"],
19
- "exclude": ["node_modules", "dist"]
20
- }
29
+ "exclude": ["node_modules", "storage"]
30
+ }
@@ -0,0 +1,42 @@
1
+ # {{projectName}}
2
+
3
+ A Strav 1.0 full-stack application scaffolded by `@strav/spring --web`.
4
+
5
+ ## Run
6
+
7
+ ```bash
8
+ bun install
9
+ bun strav view:build # compile resources/ts/islands/ into public/assets/islands/
10
+ bun strav serve # HTTP server on http://localhost:3000
11
+ ```
12
+
13
+ Open `http://localhost:3000/` — the page at `resources/views/pages/index.strav`
14
+ renders, with the Vue 3 island defined in `resources/ts/islands/counter.vue`
15
+ hydrating on load.
16
+
17
+ ## Layout
18
+
19
+ This project follows the layout in `spec/directory-structure.md`:
20
+
21
+ - `app/` — application code (`http/`, `models/`, `providers/`, …).
22
+ - `bin/strav.ts` — single dispatcher; `bun strav <command>` invokes it.
23
+ - `bootstrap/{app,providers}.ts` — Application factory + provider list.
24
+ - `config/` — typed config; one file per package, each exports `default`.
25
+ - `routes/{api,web,console}.ts` — declared route entry points.
26
+ - `resources/`
27
+ - `views/pages/**/*.strav` — auto-routed pages via `@strav/view`.
28
+ - `views/layouts/`, `views/components/`, `views/errors/` — the rest of the templates.
29
+ - `ts/islands/*.vue` + `setup.ts` — Vue 3 islands. Bundled into one
30
+ `islands.js` by `bun strav view:build`.
31
+ - `css/app.css` — base stylesheet (plain CSS; swap in Tailwind /
32
+ vanilla-extract / your choice).
33
+ - `public/` — static assets served by `routes/web.ts`. `public/assets/`
34
+ receives the islands bundle output.
35
+ - `tests/feature/` — HTTP-level integration tests.
36
+ - `tests/browser/` — Playwright-driven (you install Playwright when you need it).
37
+
38
+ ## Adding more packages
39
+
40
+ The scaffolded app starts with `@strav/{kernel,http,view,cli}`. Add
41
+ `@strav/database`, `@strav/auth`, `@strav/queue`, `@strav/broadcast` (and
42
+ their config + provider entries in `bootstrap/providers.ts`) as you need them.
@@ -0,0 +1,13 @@
1
+ node_modules
2
+ .env
3
+ .env.local
4
+ storage/cache/*
5
+ !storage/cache/.gitkeep
6
+ storage/logs/*
7
+ !storage/logs/.gitkeep
8
+ storage/uploads/*
9
+ !storage/uploads/.gitkeep
10
+ public/assets/islands/
11
+ public/assets/app.css
12
+ *.log
13
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ import { Router } from '@strav/http'
2
+ import { type Application, ServiceProvider } from '@strav/kernel'
3
+ import { registerApiRoutes } from '../../routes/api.ts'
4
+ import { registerWebRoutes } from '../../routes/web.ts'
5
+
6
+ /**
7
+ * Application-level wiring: registers routes, app-owned bindings, custom
8
+ * middleware. Runs in `register()` so the router still accepts route
9
+ * additions — `HttpProvider.boot()` compiles the trie + locks the
10
+ * registry; `ViewProvider.boot()` then layers auto-routed pages on top.
11
+ */
12
+ export class AppProvider extends ServiceProvider {
13
+ override readonly name = 'app'
14
+ override readonly dependencies = ['http']
15
+
16
+ override register(app: Application): void {
17
+ const router = app.resolve(Router)
18
+ registerApiRoutes(router)
19
+ registerWebRoutes(router)
20
+ }
21
+ }
@@ -0,0 +1,34 @@
1
+ import { HttpConsoleProvider, HttpProvider } from '@strav/http'
2
+ import { ConfigProvider, LoggerProvider, type ServiceProvider } from '@strav/kernel'
3
+ import { ViewConsoleProvider, ViewProvider } from '@strav/view'
4
+ import { AppProvider } from '../app/providers/app_provider.ts'
5
+ import appConfig from '../config/app.ts'
6
+ import httpConfig from '../config/http.ts'
7
+ import loggerConfig from '../config/logger.ts'
8
+ import viewConfig from '../config/view.ts'
9
+
10
+ /**
11
+ * Default provider list. Order is not load-bearing — the container does a
12
+ * dependency-aware topo sort at boot. Keep providers grouped by package
13
+ * for readability.
14
+ *
15
+ * `ViewProvider` discovers `.strav` files under `resources/views/pages/`
16
+ * and registers a route for each at boot. `ViewConsoleProvider` adds the
17
+ * `view:build` / `view:cache` / `view:clear` commands.
18
+ */
19
+ export function providers(): ServiceProvider[] {
20
+ return [
21
+ new ConfigProvider({
22
+ app: appConfig,
23
+ http: httpConfig,
24
+ logger: loggerConfig,
25
+ view: viewConfig,
26
+ }),
27
+ new LoggerProvider(),
28
+ new HttpProvider(),
29
+ new HttpConsoleProvider(),
30
+ new ViewProvider(),
31
+ new ViewConsoleProvider(),
32
+ new AppProvider(),
33
+ ]
34
+ }
@@ -1,11 +1,20 @@
1
1
  import { env } from '@strav/kernel'
2
2
 
3
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
-
9
- // Full application URL (optional - will be constructed from host/port/domain if not set)
10
- app_url: env('APP_URL'),
11
- }
4
+ /**
5
+ * Expose stack traces in error responses. Convenient locally; never
6
+ * leave this on in production.
7
+ */
8
+ exposeStackTrace: env('APP_ENV', 'local') !== 'production',
9
+ /**
10
+ * Static-asset root. GET / HEAD requests that no route handles fall
11
+ * through to a file lookup under this directory before the 404 path
12
+ * runs. Path traversal (`..`) is rejected by the kernel.
13
+ *
14
+ * `bun strav view:build` writes the islands bundle into
15
+ * `public/assets/islands/` — make sure your stylesheet build does the
16
+ * same so the layout's `<link rel="stylesheet" href="/assets/app.css">`
17
+ * resolves.
18
+ */
19
+ publicDir: 'public',
20
+ }
@@ -1,12 +1,12 @@
1
1
  import { env } from '@strav/kernel'
2
+ import type { ViewConfig } from '@strav/view'
2
3
 
3
4
  export default {
4
5
  directory: 'resources/views',
5
- cache: env.bool('VIEW_CACHE', true),
6
- assets: ['css/app.css', 'builds/islands.js'],
6
+ cache: env('APP_ENV', 'local') === 'production',
7
+ islandsDir: 'resources/ts/islands',
8
+ islandsOut: 'public/assets/islands',
7
9
  pages: {
8
- directory: 'pages',
9
- enabled: true,
10
- indexFile: 'index.strav'
11
- }
12
- }
10
+ autoRoute: true,
11
+ },
12
+ } satisfies ViewConfig
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "strav": "bun bin/strav.ts",
8
+ "dev": "bun --hot bin/strav.ts serve",
9
+ "build": "bun bin/strav.ts view:build",
10
+ "test": "bun test"
11
+ },
12
+ "dependencies": {
13
+ "@strav/cli": "{{stravVersion}}",
14
+ "@strav/http": "{{stravVersion}}",
15
+ "@strav/kernel": "{{stravVersion}}",
16
+ "@strav/view": "{{stravVersion}}",
17
+ "vue": "^3.5.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/bun": "latest",
21
+ "@vue/compiler-sfc": "^3.5.0"
22
+ },
23
+ "engines": {
24
+ "bun": ">=1.3.14"
25
+ }
26
+ }
File without changes
@@ -0,0 +1,41 @@
1
+ /* Bare-bones starter styles. Swap for Tailwind, vanilla-extract, or your
2
+ * stylesheet of choice — just point the <link rel="stylesheet"> in
3
+ * `resources/views/layouts/app.strav` at the right built output. */
4
+
5
+ :root {
6
+ --bg: #ffffff;
7
+ --fg: #111111;
8
+ --accent: #3b82f6;
9
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
10
+ line-height: 1.5;
11
+ color-scheme: light dark;
12
+ }
13
+
14
+ @media (prefers-color-scheme: dark) {
15
+ :root {
16
+ --bg: #0f0f10;
17
+ --fg: #f5f5f5;
18
+ }
19
+ }
20
+
21
+ body {
22
+ margin: 0;
23
+ background: var(--bg);
24
+ color: var(--fg);
25
+ }
26
+
27
+ main {
28
+ max-width: 48rem;
29
+ margin: 0 auto;
30
+ padding: 2rem 1.25rem;
31
+ }
32
+
33
+ button {
34
+ cursor: pointer;
35
+ border-radius: 0.375rem;
36
+ border: 1px solid var(--accent);
37
+ background: var(--accent);
38
+ color: #fff;
39
+ padding: 0.5rem 1rem;
40
+ font: inherit;
41
+ }
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+
4
+ const props = defineProps<{ start?: number }>()
5
+ // biome-ignore lint/correctness/noUnusedVariables: bound by the <template> below
6
+ const count = ref(props.start ?? 0)
7
+ </script>
8
+
9
+ <template>
10
+ <button @click="count++">Count is {{ count }}</button>
11
+ </template>
@@ -0,0 +1,13 @@
1
+ import type { App } from 'vue'
2
+
3
+ /**
4
+ * Shared setup for every Vue island on the page. Runs once on the single
5
+ * `createApp(Root)` that the `@strav/view` islands bundler produces, so
6
+ * plugins (Pinia, vue-router, i18n, …) and global directives go here.
7
+ *
8
+ * Export a default function. The bundler invokes it with the app instance.
9
+ */
10
+ export default (app: App): void => {
11
+ // Example: app.use(createPinia())
12
+ void app
13
+ }
@@ -0,0 +1,8 @@
1
+ @extends('layouts.app')
2
+
3
+ @set('title', 'Not found')
4
+
5
+ @section('content')
6
+ <h1>404 — Not found</h1>
7
+ <p>The page you asked for doesn't exist.</p>
8
+ @endsection
@@ -0,0 +1,8 @@
1
+ @extends('layouts.app')
2
+
3
+ @set('title', 'Server error')
4
+
5
+ @section('content')
6
+ <h1>500 — Something went wrong</h1>
7
+ <p>An unexpected error occurred. Please try again shortly.</p>
8
+ @endsection
@@ -0,0 +1,15 @@
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" />
6
+ <title>@yield('title', '{{projectName}}')</title>
7
+ <link rel="stylesheet" href="/assets/app.css" />
8
+ </head>
9
+ <body>
10
+ <main>
11
+ @yield('content')
12
+ </main>
13
+ <script type="module" src="/assets/islands/islands.js" defer></script>
14
+ </body>
15
+ </html>
@@ -0,0 +1,12 @@
1
+ @extends('layouts.app')
2
+
3
+ @set('title', '{{projectName}}')
4
+
5
+ @section('content')
6
+ <h1>Welcome to {{projectName}}</h1>
7
+ <p>You're looking at <code>resources/views/pages/index.strav</code>.</p>
8
+
9
+ <p>The button below is a Vue 3 island — see <code>resources/ts/islands/counter.vue</code>. Run <code>bun strav view:build</code> to compile islands into <code>public/assets/islands/</code>.</p>
10
+
11
+ @island('Counter', { start: 0 })
12
+ @endsection
@@ -0,0 +1,9 @@
1
+ /**
2
+ * SSE / broadcast channel declarations. Empty until the app installs
3
+ * `@strav/broadcast` and declares its first channel — at which point
4
+ * a `Broadcaster` instance + per-channel authorize callbacks live here.
5
+ *
6
+ * Spec anchor (`spec/directory-structure.md` lists `routes/broadcast.ts`)
7
+ * but slice B doesn't ship the `@strav/broadcast` integration.
8
+ */
9
+ export {}
@@ -0,0 +1,19 @@
1
+ import type { Router } from '@strav/http'
2
+
3
+ /**
4
+ * HTML / browser-facing routes that need to be hand-declared (login forms,
5
+ * dynamic redirects, sitemap.xml — anything that can't be a static
6
+ * `.strav` page).
7
+ *
8
+ * Two things you may NOT need to wire here:
9
+ *
10
+ * 1. **Auto-routed pages** — `@strav/view` registers a GET route for
11
+ * every `.strav` file under `resources/views/pages/` automatically.
12
+ * 2. **Static assets** — set `publicDir: 'public'` in `config/http.ts`
13
+ * (already wired in the scaffolded config) and the HTTP kernel will
14
+ * serve files from `public/` for any unrouted GET / HEAD request.
15
+ * Path traversal is rejected; routed paths still win over disk.
16
+ */
17
+ export function registerWebRoutes(_router: Router): void {
18
+ // Hand-declared web routes go here.
19
+ }
File without changes
package/src/version.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Framework version that scaffolded apps pin to in their generated
3
+ * `package.json`. Bumped manually at release; see the template-strategy ADR.
4
+ *
5
+ * Spring's own `package.json` version tracks the workspace alpha for now,
6
+ * but this constant is what *generated apps* depend on — keep them in sync
7
+ * on each release until spring formally cuts independent versions.
8
+ */
9
+ export const STRAV_VERSION = '^1.0.0-alpha.28'
@@ -1,15 +0,0 @@
1
- import type { Context } from '@strav/http'
2
-
3
- export abstract class Controller {
4
- protected async respond<T>(ctx: Context, data: T, status = 200) {
5
- return ctx.json(data, status)
6
- }
7
-
8
- protected async error(ctx: Context, message: string, status = 400) {
9
- return ctx.json({ error: message }, status)
10
- }
11
-
12
- protected async notFound(ctx: Context, message = 'Not found') {
13
- return ctx.json({ error: message }, 404)
14
- }
15
- }
@@ -1,69 +0,0 @@
1
- import type { Context } from '@strav/http'
2
- import { Controller } from './controller.ts'
3
- import User from '../models/user.ts'
4
-
5
- export default class UserController extends Controller {
6
- async index(ctx: Context) {
7
- const users = await User.all()
8
- return this.respond(ctx, { users })
9
- }
10
-
11
- async show(ctx: Context) {
12
- const { id } = ctx.params
13
- const user = await User.find(id)
14
-
15
- if (!user) {
16
- return this.notFound(ctx, 'User not found')
17
- }
18
-
19
- return this.respond(ctx, { user })
20
- }
21
-
22
- async store(ctx: Context) {
23
- const { email, name, password } = await ctx.request.json()
24
-
25
- if (!email || !name || !password) {
26
- return this.error(ctx, 'Email, name, and password are required')
27
- }
28
-
29
- const user = await User.create({
30
- id: crypto.randomUUID(),
31
- email,
32
- name,
33
- password_hash: await Bun.password.hash(password),
34
- })
35
-
36
- return this.respond(ctx, { user }, 201)
37
- }
38
-
39
- async update(ctx: Context) {
40
- const { id } = ctx.params
41
- const user = await User.find(id)
42
-
43
- if (!user) {
44
- return this.notFound(ctx, 'User not found')
45
- }
46
-
47
- const { email, name } = await ctx.request.json()
48
-
49
- if (email) user.email = email
50
- if (name) user.name = name
51
-
52
- await user.save()
53
-
54
- return this.respond(ctx, { user })
55
- }
56
-
57
- async destroy(ctx: Context) {
58
- const { id } = ctx.params
59
- const user = await User.find(id)
60
-
61
- if (!user) {
62
- return this.notFound(ctx, 'User not found')
63
- }
64
-
65
- await user.delete()
66
-
67
- return this.respond(ctx, { message: 'User deleted successfully' })
68
- }
69
- }
@@ -1,9 +0,0 @@
1
- import { env } from '@strav/kernel'
2
-
3
- export default {
4
- host: env('DB_HOST', 'localhost'),
5
- port: env.int('DB_PORT', 5432),
6
- database: env('DB_DATABASE', '__DB_NAME__'),
7
- username: env('DB_USER', 'postgres'),
8
- password: env('DB_PASSWORD', ''),
9
- }
@@ -1,17 +0,0 @@
1
- import { env } from '@strav/kernel'
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
-
9
- // Full application URL (optional - will be constructed from host/port/domain if not set)
10
- app_url: env('APP_URL'),
11
-
12
- cors: {
13
- enabled: true,
14
- origin: ['http://localhost:3000', 'http://localhost:5173'],
15
- credentials: true,
16
- },
17
- }
@@ -1,11 +0,0 @@
1
- import { Factory } from '@strav/testing'
2
- import User from '../../app/models/user.ts'
3
-
4
- export const UserFactory = Factory.define(User, (seq) => ({
5
- id: crypto.randomUUID(),
6
- email: `user-${seq}@example.com`,
7
- name: `User ${seq}`,
8
- password_hash: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
9
- email_verified_at: new Date(),
10
- remember_token: null,
11
- }))
@@ -1,13 +0,0 @@
1
- import { defineSchema, t, Archetype } from '@strav/database'
2
-
3
- export default defineSchema('user', {
4
- archetype: Archetype.Entity,
5
- fields: {
6
- id: t.uuid().primaryKey(),
7
- email: t.string().email().unique().required(),
8
- name: t.string().required(),
9
- password_hash: t.string().required(),
10
- email_verified_at: t.timestamp().nullable(),
11
- remember_token: t.string(100).nullable(),
12
- },
13
- })
@@ -1,8 +0,0 @@
1
- import { Seeder } from '@strav/database'
2
- import UserSeeder from './user_seeder.ts'
3
-
4
- export default class DatabaseSeeder extends Seeder {
5
- async run(): Promise<void> {
6
- await this.call(UserSeeder)
7
- }
8
- }
@@ -1,15 +0,0 @@
1
- import { Seeder } from '@strav/database'
2
- import { UserFactory } from '../factories/user_factory.ts'
3
-
4
- export default class UserSeeder extends Seeder {
5
- async run(): Promise<void> {
6
- // Create admin user
7
- await UserFactory.create({
8
- email: 'admin@example.com',
9
- name: 'Admin User',
10
- })
11
-
12
- // Create test users
13
- await UserFactory.createMany(10)
14
- }
15
- }
@@ -1,11 +0,0 @@
1
- import 'reflect-metadata'
2
- import { app } from '@strav/kernel'
3
- import { providers } from './start/providers'
4
-
5
- // Register service providers
6
- app.useProviders(providers)
7
-
8
- // Load routes
9
- await import('./start/routes')
10
-
11
- await app.start()
@@ -1,24 +0,0 @@
1
- {
2
- "name": "__PROJECT_NAME__",
3
- "version": "0.1.0",
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
- "typecheck": "tsc --noEmit"
11
- },
12
- "dependencies": {
13
- "@strav/kernel": "__STRAV_VERSION__",
14
- "@strav/http": "__STRAV_VERSION__",
15
- "@strav/database": "__STRAV_VERSION__",
16
- "@strav/cli": "__STRAV_VERSION__",
17
- "reflect-metadata": "^0.2.2"
18
- },
19
- "devDependencies": {
20
- "@types/bun": "latest",
21
- "@strav/testing": "__STRAV_VERSION__",
22
- "typescript": "^5.9.3"
23
- }
24
- }
@@ -1,10 +0,0 @@
1
- import { DatabaseProvider } from "@strav/database"
2
- import { HttpProvider } from "@strav/http"
3
- import { ConfigProvider, EncryptionProvider, ServiceProvider } from "@strav/kernel"
4
-
5
- export const providers: ServiceProvider[] = [
6
- new ConfigProvider(),
7
- new HttpProvider(),
8
- new DatabaseProvider(),
9
- new EncryptionProvider(),
10
- ]