alepha 0.14.4 → 0.15.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 +1 -4
- package/dist/api/audits/index.d.ts +619 -731
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +185 -298
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +0 -1
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +245 -356
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/notifications/index.d.ts +238 -350
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/parameters/index.d.ts +499 -611
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.browser.js +1 -2
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1697 -1804
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +178 -151
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +132 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.d.ts +122 -122
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +1 -2
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +163 -163
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/cache/core/index.d.ts +46 -46
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cli/index.d.ts +302 -299
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +966 -564
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +303 -299
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +11 -7
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +419 -99
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +718 -625
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +420 -99
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +419 -99
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +44 -44
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +4 -4
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +97 -50
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +129 -33
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +7981 -14
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/file/index.d.ts +523 -390
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +253 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +208 -208
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +25 -26
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/mcp/index.d.ts +197 -197
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/orm/chunk-DtkW-qnP.js +38 -0
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +2814 -0
- package/dist/orm/index.bun.js.map +1 -0
- package/dist/orm/index.d.ts +1205 -1057
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +2056 -1753
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +248 -248
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/redis/index.bun.js +285 -0
- package/dist/redis/index.bun.js.map +1 -0
- package/dist/redis/index.d.ts +118 -136
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +18 -38
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +69 -69
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/router/index.d.ts +6 -6
- package/dist/router/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +25 -25
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/security/index.browser.js +5 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +417 -254
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +386 -86
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +277 -277
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +20 -20
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +60 -57
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +3 -3
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/cookies/index.d.ts +6 -6
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +242 -150
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +288 -122
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +11 -12
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +0 -1
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/helmet/index.d.ts +2 -2
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +84 -85
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +1 -2
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/multipart/index.d.ts +6 -6
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +102 -103
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts +16 -16
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/static/index.d.ts +44 -44
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +48 -49
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1 -2
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +13 -11
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +7 -7
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +71 -72
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/topic/core/index.d.ts +318 -318
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts +6 -6
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/vite/index.d.ts +5720 -159
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +41 -18
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +6 -6
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +247 -247
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +6 -6
- package/dist/websocket/index.js.map +1 -1
- package/package.json +9 -14
- package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
- package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
- package/src/api/users/entities/users.ts +1 -1
- package/src/api/users/index.ts +8 -8
- package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
- package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
- package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
- package/src/api/users/services/CredentialService.ts +7 -7
- package/src/api/users/services/IdentityService.ts +4 -4
- package/src/api/users/services/RegistrationService.spec.ts +25 -27
- package/src/api/users/services/RegistrationService.ts +38 -27
- package/src/api/users/services/SessionCrudService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +3 -3
- package/src/api/users/services/SessionService.ts +28 -9
- package/src/api/users/services/UserService.ts +7 -7
- package/src/batch/providers/BatchProvider.ts +1 -2
- package/src/cli/apps/AlephaPackageBuilderCli.ts +38 -19
- package/src/cli/assets/apiHelloControllerTs.ts +18 -0
- package/src/cli/assets/apiIndexTs.ts +16 -0
- package/src/cli/assets/claudeMd.ts +303 -0
- package/src/cli/assets/mainBrowserTs.ts +2 -2
- package/src/cli/assets/mainServerTs.ts +24 -0
- package/src/cli/assets/webAppRouterTs.ts +15 -0
- package/src/cli/assets/webHelloComponentTsx.ts +16 -0
- package/src/cli/assets/webIndexTs.ts +16 -0
- package/src/cli/commands/build.ts +41 -21
- package/src/cli/commands/db.ts +21 -18
- package/src/cli/commands/deploy.ts +17 -5
- package/src/cli/commands/dev.ts +13 -17
- package/src/cli/commands/format.ts +8 -2
- package/src/cli/commands/init.ts +74 -29
- package/src/cli/commands/lint.ts +8 -2
- package/src/cli/commands/test.ts +8 -2
- package/src/cli/commands/typecheck.ts +5 -1
- package/src/cli/commands/verify.ts +4 -2
- package/src/cli/services/AlephaCliUtils.ts +39 -600
- package/src/cli/services/PackageManagerUtils.ts +301 -0
- package/src/cli/services/ProjectScaffolder.ts +306 -0
- package/src/command/helpers/Runner.ts +15 -3
- package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +2 -0
- package/src/core/primitives/$hook.ts +6 -2
- package/src/core/primitives/$module.spec.ts +4 -0
- package/src/core/providers/AlsProvider.ts +1 -1
- package/src/core/providers/CodecManager.spec.ts +12 -6
- package/src/core/providers/CodecManager.ts +26 -6
- package/src/core/providers/EventManager.ts +169 -13
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +621 -0
- package/src/core/providers/KeylessJsonSchemaCodec.ts +407 -0
- package/src/core/providers/StateManager.spec.ts +27 -16
- package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
- package/src/email/providers/LocalEmailProvider.ts +52 -15
- package/src/email/providers/NodemailerEmailProvider.ts +167 -56
- package/src/file/errors/FileError.ts +7 -0
- package/src/file/index.ts +9 -1
- package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
- package/src/orm/index.browser.ts +1 -19
- package/src/orm/index.bun.ts +77 -0
- package/src/orm/index.shared-server.ts +22 -0
- package/src/orm/index.shared.ts +15 -0
- package/src/orm/index.ts +19 -39
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
- package/src/orm/services/Repository.ts +8 -0
- package/src/redis/index.bun.ts +35 -0
- package/src/redis/providers/BunRedisProvider.ts +12 -43
- package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
- package/src/redis/providers/NodeRedisProvider.ts +16 -34
- package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
- package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
- package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
- package/src/security/index.browser.ts +5 -0
- package/src/security/index.ts +90 -7
- package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
- package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
- package/src/security/primitives/$role.ts +5 -5
- package/src/security/primitives/$serviceAccount.spec.ts +5 -5
- package/src/security/primitives/$serviceAccount.ts +3 -3
- package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
- package/src/server/auth/primitives/$auth.ts +10 -10
- package/src/server/auth/primitives/$authCredentials.ts +3 -3
- package/src/server/auth/primitives/$authGithub.ts +3 -3
- package/src/server/auth/primitives/$authGoogle.ts +3 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
- package/src/server/cache/providers/ServerCacheProvider.ts +1 -1
- package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
- package/src/server/core/providers/NodeHttpServerProvider.ts +25 -6
- package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
- package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
- package/src/server/core/providers/ServerProvider.ts +144 -21
- package/src/server/core/providers/ServerRouterProvider.ts +259 -115
- package/src/server/core/providers/ServerTimingProvider.ts +2 -2
- package/src/server/links/index.ts +1 -1
- package/src/server/links/providers/LinkProvider.ts +1 -1
- package/src/server/swagger/index.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
- package/src/sms/providers/LocalSmsProvider.ts +8 -7
- package/src/vite/helpers/boot.ts +28 -17
- package/src/vite/tasks/buildServer.ts +12 -1
- package/src/vite/tasks/devServer.ts +3 -1
- package/src/vite/tasks/generateCloudflare.ts +7 -0
- package/dist/server/security/index.browser.js +0 -13
- package/dist/server/security/index.browser.js.map +0 -1
- package/dist/server/security/index.d.ts +0 -173
- package/dist/server/security/index.d.ts.map +0 -1
- package/dist/server/security/index.js +0 -311
- package/dist/server/security/index.js.map +0 -1
- package/src/cli/assets/appRouterTs.ts +0 -9
- package/src/cli/assets/mainTs.ts +0 -13
- package/src/server/security/index.browser.ts +0 -10
- package/src/server/security/index.ts +0 -94
- /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
- /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
export interface ClaudeMdOptions {
|
|
2
|
+
react?: boolean;
|
|
3
|
+
ui?: boolean;
|
|
4
|
+
projectName?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const claudeMd = (options: ClaudeMdOptions = {}) => {
|
|
8
|
+
const { react = false, projectName = "my-app" } = options;
|
|
9
|
+
|
|
10
|
+
const reactSection = react
|
|
11
|
+
? `
|
|
12
|
+
## React & Frontend
|
|
13
|
+
|
|
14
|
+
### Pages with \`$page\`
|
|
15
|
+
\`\`\`tsx
|
|
16
|
+
import { $page } from "@alepha/react/router";
|
|
17
|
+
import { $client } from "alepha/server/links";
|
|
18
|
+
import type { UserController } from "./UserController.ts";
|
|
19
|
+
|
|
20
|
+
class AppRouter {
|
|
21
|
+
api = $client<UserController>();
|
|
22
|
+
|
|
23
|
+
users = $page({
|
|
24
|
+
path: "/users",
|
|
25
|
+
loader: async () => ({ users: await this.api.listUsers() }),
|
|
26
|
+
component: ({ users }) => (
|
|
27
|
+
<ul>{users.map(u => <li key={u.id}>{u.email}</li>)}</ul>
|
|
28
|
+
),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
userDetail = $page({
|
|
32
|
+
path: "/users/:id",
|
|
33
|
+
schema: { params: t.object({ id: t.uuid() }) },
|
|
34
|
+
loader: async ({ params }) => ({ user: await this.api.getUser({ params }) }),
|
|
35
|
+
lazy: () => import("./UserDetail.tsx"), // Code splitting
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
### React Hooks
|
|
41
|
+
\`\`\`typescript
|
|
42
|
+
import { useAlepha, useClient, useStore, useAction, useInject } from "@alepha/react";
|
|
43
|
+
import { useRouter, useActive } from "@alepha/react/router";
|
|
44
|
+
import { useForm } from "@alepha/react/form";
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
- \`useClient<Controller>()\` - Type-safe API calls
|
|
48
|
+
- \`useStore(atom)\` - Global state (returns \`[value, setValue]\`)
|
|
49
|
+
- \`useAction({ handler })\` - Async operations with loading/error state
|
|
50
|
+
- \`useRouter<AppRouter>()\` - Type-safe navigation
|
|
51
|
+
- \`useForm({ schema, handler })\` - Type-safe forms with validation
|
|
52
|
+
`
|
|
53
|
+
: "";
|
|
54
|
+
|
|
55
|
+
const projectStructure = react
|
|
56
|
+
? `
|
|
57
|
+
\`\`\`
|
|
58
|
+
${projectName}/
|
|
59
|
+
├── src/
|
|
60
|
+
│ ├── api/ # Backend
|
|
61
|
+
│ │ ├── controllers/ # API controllers with $action
|
|
62
|
+
│ │ ├── services/ # Business logic
|
|
63
|
+
│ │ ├── entities/ # Database entities with $entity
|
|
64
|
+
│ │ ├── providers/ # External service wrappers
|
|
65
|
+
│ │ └── index.ts # API module definition with $module
|
|
66
|
+
│ ├── web/ # Frontend (React only)
|
|
67
|
+
│ │ ├── components/ # React components
|
|
68
|
+
│ │ ├── atoms/ # State atoms with $atom
|
|
69
|
+
│ │ ├── AppRouter.ts # Routes with $page
|
|
70
|
+
│ │ └── index.ts # Web module definition with $module
|
|
71
|
+
│ ├── main.server.ts # Server entry
|
|
72
|
+
│ └── main.browser.ts # Browser entry (React only)
|
|
73
|
+
├── index.html # (React only)
|
|
74
|
+
├── package.json
|
|
75
|
+
└── tsconfig.json
|
|
76
|
+
\`\`\`
|
|
77
|
+
`
|
|
78
|
+
: `
|
|
79
|
+
\`\`\`
|
|
80
|
+
${projectName}/
|
|
81
|
+
├── src/
|
|
82
|
+
│ ├── api/ # Backend
|
|
83
|
+
│ │ ├── controllers/ # API controllers with $action
|
|
84
|
+
│ │ ├── services/ # Business logic
|
|
85
|
+
│ │ ├── entities/ # Database entities with $entity
|
|
86
|
+
│ │ ├── providers/ # External service wrappers
|
|
87
|
+
│ │ └── index.ts # API module definition with $module
|
|
88
|
+
│ └── main.server.ts # Server entry (always use main.server.ts)
|
|
89
|
+
├── package.json
|
|
90
|
+
└── tsconfig.json
|
|
91
|
+
\`\`\`
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
return `# CLAUDE.md
|
|
95
|
+
|
|
96
|
+
This file provides guidance to Claude Code when working with this Alepha project.
|
|
97
|
+
|
|
98
|
+
## Overview
|
|
99
|
+
|
|
100
|
+
This is an **Alepha** project - a convention-driven TypeScript framework for type-safe full-stack applications.
|
|
101
|
+
|
|
102
|
+
**Key Concepts:**
|
|
103
|
+
- **Primitives**: Features defined with \`$\`-prefixed functions (\`$action\`, \`$entity\`, \`$page\`)
|
|
104
|
+
- **Class-Based**: Services are classes, primitives are class properties
|
|
105
|
+
- **Zero-Config**: Code structure IS the configuration
|
|
106
|
+
- **End-to-End Types**: Types flow from database → API → ${react ? "React" : "client"}
|
|
107
|
+
|
|
108
|
+
## Rules
|
|
109
|
+
|
|
110
|
+
- Use TypeScript strict mode
|
|
111
|
+
- Use Biome for formatting (\`alepha lint\`)
|
|
112
|
+
- Use Vitest for testing
|
|
113
|
+
- One file = one class
|
|
114
|
+
- Primitives are class properties (except \`$entity\`, \`$atom\`)
|
|
115
|
+
- No decorators, no Express/Fastify patterns
|
|
116
|
+
- No manual instantiation - use dependency injection
|
|
117
|
+
- Use \`protected\` instead of \`private\` for class members
|
|
118
|
+
- Import with file extensions: \`import { User } from "./User.ts"\`
|
|
119
|
+
- Use \`t\` from Alepha for schemas (not Zod)
|
|
120
|
+
|
|
121
|
+
## Project Structure
|
|
122
|
+
${projectStructure}
|
|
123
|
+
## Core Primitives
|
|
124
|
+
|
|
125
|
+
### API with \`$action\`
|
|
126
|
+
\`\`\`typescript
|
|
127
|
+
import { t } from "alepha";
|
|
128
|
+
import { $action } from "alepha/server";
|
|
129
|
+
|
|
130
|
+
class UserController {
|
|
131
|
+
getUser = $action({
|
|
132
|
+
path: "/users/:id", // → GET /api/users/:id
|
|
133
|
+
schema: {
|
|
134
|
+
params: t.object({ id: t.uuid() }),
|
|
135
|
+
response: t.object({ id: t.uuid(), email: t.email() }),
|
|
136
|
+
},
|
|
137
|
+
handler: async ({ params }) => this.userRepo.findById(params.id),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
createUser = $action({
|
|
141
|
+
// POST inferred from body schema
|
|
142
|
+
schema: {
|
|
143
|
+
body: t.object({ email: t.email() }),
|
|
144
|
+
response: userEntity.schema,
|
|
145
|
+
},
|
|
146
|
+
handler: async ({ body }) => this.userRepo.create(body),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
### Database with \`$entity\` and \`$repository\`
|
|
152
|
+
\`\`\`typescript
|
|
153
|
+
import { $entity, $repository, db } from "alepha/orm";
|
|
154
|
+
|
|
155
|
+
// Entity defined at module level (for drizzle-kit compatibility)
|
|
156
|
+
export const userEntity = $entity({
|
|
157
|
+
name: "users",
|
|
158
|
+
schema: t.object({
|
|
159
|
+
id: db.primaryKey(),
|
|
160
|
+
email: t.email(),
|
|
161
|
+
createdAt: db.createdAt(),
|
|
162
|
+
updatedAt: db.updatedAt(),
|
|
163
|
+
}),
|
|
164
|
+
indexes: [{ column: "email", unique: true }],
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
class UserService {
|
|
168
|
+
repo = $repository(userEntity);
|
|
169
|
+
|
|
170
|
+
async findById(id: string) {
|
|
171
|
+
return this.repo.findById(id);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
\`\`\`
|
|
175
|
+
|
|
176
|
+
### Dependency Injection
|
|
177
|
+
\`\`\`typescript
|
|
178
|
+
import { $inject } from "alepha";
|
|
179
|
+
|
|
180
|
+
class OrderService {
|
|
181
|
+
userService = $inject(UserService); // Within same module
|
|
182
|
+
|
|
183
|
+
async createOrder(userId: string) {
|
|
184
|
+
const user = await this.userService.findById(userId);
|
|
185
|
+
// ...
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Cross-module: use $client instead of $inject
|
|
190
|
+
class AppRouter {
|
|
191
|
+
api = $client<OrderController>(); // Type-safe HTTP client
|
|
192
|
+
}
|
|
193
|
+
\`\`\`
|
|
194
|
+
|
|
195
|
+
### Modules with \`$module\`
|
|
196
|
+
\`\`\`typescript
|
|
197
|
+
// src/api/index.ts - Groups all API services
|
|
198
|
+
import { $module } from "alepha";
|
|
199
|
+
|
|
200
|
+
export const ApiModule = $module({
|
|
201
|
+
name: "app.api",
|
|
202
|
+
services: [
|
|
203
|
+
UserController,
|
|
204
|
+
OrderController,
|
|
205
|
+
UserService,
|
|
206
|
+
],
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// src/web/index.ts - Groups all web services (React only)
|
|
210
|
+
export const WebModule = $module({
|
|
211
|
+
name: "app.web",
|
|
212
|
+
services: [AppRouter, Toaster],
|
|
213
|
+
register(alepha) {
|
|
214
|
+
// Optional: configure additional services
|
|
215
|
+
alepha.with(SomeLibrary);
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
\`\`\`
|
|
219
|
+
|
|
220
|
+
### Environment Variables
|
|
221
|
+
\`\`\`typescript
|
|
222
|
+
import { $env, t } from "alepha";
|
|
223
|
+
|
|
224
|
+
class AppConfig {
|
|
225
|
+
env = $env(t.object({
|
|
226
|
+
DATABASE_URL: t.string(),
|
|
227
|
+
API_KEY: t.optional(t.string()),
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
\`\`\`
|
|
231
|
+
${reactSection}
|
|
232
|
+
## Quick Reference
|
|
233
|
+
|
|
234
|
+
| Primitive | Import | Purpose |
|
|
235
|
+
|-----------|--------|---------|
|
|
236
|
+
| \`$inject\` | \`alepha\` | Dependency injection |
|
|
237
|
+
| \`$env\` | \`alepha\` | Environment variables |
|
|
238
|
+
| \`$hook\` | \`alepha\` | Lifecycle hooks |
|
|
239
|
+
| \`$logger\` | \`alepha/logger\` | Structured logging |
|
|
240
|
+
| \`$action\` | \`alepha/server\` | REST API endpoints |
|
|
241
|
+
| \`$route\` | \`alepha/server\` | Low-level HTTP routes |
|
|
242
|
+
| \`$entity\` | \`alepha/orm\` | Database tables |
|
|
243
|
+
| \`$repository\` | \`alepha/orm\` | Type-safe data access |
|
|
244
|
+
| \`$queue\` | \`alepha/queue\` | Background jobs |
|
|
245
|
+
| \`$scheduler\` | \`alepha/scheduler\` | Cron tasks |
|
|
246
|
+
| \`$cache\` | \`alepha/cache\` | Cached computations |
|
|
247
|
+
| \`$bucket\` | \`alepha/bucket\` | File storage |
|
|
248
|
+
| \`$issuer\` | \`alepha/security\` | JWT tokens |
|
|
249
|
+
| \`$command\` | \`alepha/command\` | CLI commands |${react ? `
|
|
250
|
+
| \`$page\` | \`@alepha/react/router\` | React pages with SSR |
|
|
251
|
+
| \`$atom\` | \`alepha\` | Global state |` : ""}
|
|
252
|
+
|
|
253
|
+
## Testing
|
|
254
|
+
|
|
255
|
+
\`\`\`typescript
|
|
256
|
+
import { describe, it, expect } from "vitest";
|
|
257
|
+
import { Alepha } from "alepha";
|
|
258
|
+
|
|
259
|
+
describe("UserService", () => {
|
|
260
|
+
it("should create user", async () => {
|
|
261
|
+
const alepha = Alepha.create().with(UserService);
|
|
262
|
+
const service = alepha.inject(UserService);
|
|
263
|
+
|
|
264
|
+
const user = await service.create({ email: "test@example.com" });
|
|
265
|
+
expect(user.email).toBe("test@example.com");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should mock dependencies", async () => {
|
|
269
|
+
const alepha = Alepha.create()
|
|
270
|
+
.with(OrderService)
|
|
271
|
+
.with({ provide: PaymentGateway, use: MockPaymentGateway });
|
|
272
|
+
|
|
273
|
+
const service = alepha.inject(OrderService);
|
|
274
|
+
// PaymentGateway is now mocked
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
\`\`\`
|
|
278
|
+
|
|
279
|
+
## Common Mistakes
|
|
280
|
+
|
|
281
|
+
1. **DON'T use decorators** - Use primitives (\`$action\`, not \`@Get()\`)
|
|
282
|
+
2. **DON'T use Zod** - Use TypeBox via \`t\` from Alepha
|
|
283
|
+
3. **DON'T use Express patterns** - No \`app.get()\`, \`router.use()\`
|
|
284
|
+
4. **DON'T inject across modules** - Use \`$client\` for cross-module calls
|
|
285
|
+
5. **DON'T use async constructors** - Use \`$hook({ on: "start" })\`
|
|
286
|
+
6. **DON'T instantiate manually** - Let DI container manage instances
|
|
287
|
+
|
|
288
|
+
## After Code Changes
|
|
289
|
+
|
|
290
|
+
Always run:
|
|
291
|
+
\`\`\`bash
|
|
292
|
+
alepha lint # Format and lint
|
|
293
|
+
alepha typecheck # Type checking
|
|
294
|
+
alepha test # Run tests (if applicable)
|
|
295
|
+
alepha build # Build the project
|
|
296
|
+
\`\`\`
|
|
297
|
+
|
|
298
|
+
## Documentation
|
|
299
|
+
|
|
300
|
+
- Full docs: https://alepha.dev/llms.txt
|
|
301
|
+
- Detailed docs: https://alepha.dev/llms-full.txt
|
|
302
|
+
`.trim();
|
|
303
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const mainBrowserTs = () => `
|
|
2
2
|
import { Alepha, run } from "alepha";
|
|
3
|
-
import {
|
|
3
|
+
import { WebModule } from "./web/index.ts";
|
|
4
4
|
|
|
5
5
|
const alepha = Alepha.create();
|
|
6
6
|
|
|
7
|
-
alepha.with(
|
|
7
|
+
alepha.with(WebModule);
|
|
8
8
|
|
|
9
9
|
run(alepha);
|
|
10
10
|
`.trim();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface MainServerTsOptions {
|
|
2
|
+
react?: boolean;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const mainServerTs = (options: MainServerTsOptions = {}) => {
|
|
6
|
+
const { react = false } = options;
|
|
7
|
+
|
|
8
|
+
const webImport = react
|
|
9
|
+
? `import { WebModule } from "./web/index.ts";\n`
|
|
10
|
+
: "";
|
|
11
|
+
|
|
12
|
+
const webWith = react ? `alepha.with(WebModule);\n` : "";
|
|
13
|
+
|
|
14
|
+
return `
|
|
15
|
+
import { Alepha, run } from "alepha";
|
|
16
|
+
import { ApiModule } from "./api/index.ts";
|
|
17
|
+
${webImport}
|
|
18
|
+
const alepha = Alepha.create();
|
|
19
|
+
|
|
20
|
+
alepha.with(ApiModule);
|
|
21
|
+
${webWith}
|
|
22
|
+
run(alepha);
|
|
23
|
+
`.trim();
|
|
24
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const webAppRouterTs = () => `
|
|
2
|
+
import { $page } from "@alepha/react/router";
|
|
3
|
+
import { $client } from "alepha/server/links";
|
|
4
|
+
import type { HelloController } from "../api/controllers/HelloController.ts";
|
|
5
|
+
|
|
6
|
+
export class AppRouter {
|
|
7
|
+
api = $client<HelloController>();
|
|
8
|
+
|
|
9
|
+
home = $page({
|
|
10
|
+
path: "/",
|
|
11
|
+
lazy: () => import("./components/Hello.tsx"),
|
|
12
|
+
loader: () => this.api.hello(),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
`.trim();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const webHelloComponentTsx = () => `
|
|
2
|
+
interface Props {
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const Hello = (props: Props) => {
|
|
7
|
+
return (
|
|
8
|
+
<div>
|
|
9
|
+
<h1>{props.message}</h1>
|
|
10
|
+
<p>Edit this component in src/web/components/Hello.tsx</p>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default Hello;
|
|
16
|
+
`.trim();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface WebIndexTsOptions {
|
|
2
|
+
appName?: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const webIndexTs = (options: WebIndexTsOptions = {}) => {
|
|
6
|
+
const { appName = "app" } = options;
|
|
7
|
+
return `
|
|
8
|
+
import { $module } from "alepha";
|
|
9
|
+
import { AppRouter } from "./AppRouter.ts";
|
|
10
|
+
|
|
11
|
+
export const WebModule = $module({
|
|
12
|
+
name: "${appName}.web",
|
|
13
|
+
services: [AppRouter],
|
|
14
|
+
});
|
|
15
|
+
`.trim();
|
|
16
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { access, readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { $inject, $use, t } from "alepha";
|
|
4
2
|
import { $command } from "alepha/command";
|
|
3
|
+
import { FileSystemProvider } from "alepha/file";
|
|
5
4
|
import { $logger } from "alepha/logger";
|
|
6
5
|
import {
|
|
7
6
|
boot,
|
|
@@ -16,10 +15,15 @@ import {
|
|
|
16
15
|
} from "alepha/vite";
|
|
17
16
|
import { buildOptions } from "../atoms/buildOptions.ts";
|
|
18
17
|
import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
|
|
18
|
+
import { PackageManagerUtils } from "../services/PackageManagerUtils.ts";
|
|
19
|
+
import { ProjectScaffolder } from "../services/ProjectScaffolder.ts";
|
|
19
20
|
|
|
20
21
|
export class BuildCommand {
|
|
21
22
|
protected readonly log = $logger();
|
|
23
|
+
protected readonly fs = $inject(FileSystemProvider);
|
|
22
24
|
protected readonly utils = $inject(AlephaCliUtils);
|
|
25
|
+
protected readonly pm = $inject(PackageManagerUtils);
|
|
26
|
+
protected readonly scaffolder = $inject(ProjectScaffolder);
|
|
23
27
|
protected readonly options = $use(buildOptions);
|
|
24
28
|
|
|
25
29
|
public readonly build = $command({
|
|
@@ -55,18 +59,23 @@ export class BuildCommand {
|
|
|
55
59
|
description: "Generate sitemap.xml with base URL",
|
|
56
60
|
}),
|
|
57
61
|
),
|
|
62
|
+
bun: t.optional(
|
|
63
|
+
t.boolean({
|
|
64
|
+
description: "Prioritize .bun.ts entry files for Bun runtime",
|
|
65
|
+
}),
|
|
66
|
+
),
|
|
58
67
|
}),
|
|
59
68
|
handler: async ({ flags, args, run, root }) => {
|
|
60
69
|
// Tell viteAlephaBuild plugin to skip - CLI handles all tasks
|
|
61
70
|
process.env.ALEPHA_BUILD_MODE = "cli";
|
|
62
71
|
process.env.NODE_ENV = "production";
|
|
63
72
|
|
|
64
|
-
if (await this.
|
|
73
|
+
if (await this.pm.hasExpo(root)) {
|
|
65
74
|
// will come soon
|
|
66
75
|
return;
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
await this.
|
|
78
|
+
await this.scaffolder.ensureConfig(root, {
|
|
70
79
|
tsconfigJson: true,
|
|
71
80
|
});
|
|
72
81
|
|
|
@@ -76,21 +85,17 @@ export class BuildCommand {
|
|
|
76
85
|
const distDir = "dist";
|
|
77
86
|
const clientDir = "public";
|
|
78
87
|
|
|
79
|
-
await this.
|
|
88
|
+
await this.pm.ensureDependency(root, "vite", {
|
|
89
|
+
run,
|
|
90
|
+
exec: (cmd, opts) => this.utils.exec(cmd, opts),
|
|
91
|
+
});
|
|
80
92
|
await run.rm("dist", { alias: "clean dist" });
|
|
81
93
|
|
|
82
94
|
const options = this.options;
|
|
83
95
|
await this.utils.loadEnv(root, [".env", ".env.production"]);
|
|
84
96
|
|
|
85
97
|
const stats = flags.stats ?? options.stats ?? false;
|
|
86
|
-
|
|
87
|
-
let hasClient = false;
|
|
88
|
-
try {
|
|
89
|
-
await access(join(root, "index.html"));
|
|
90
|
-
hasClient = true;
|
|
91
|
-
} catch {
|
|
92
|
-
// No index.html
|
|
93
|
-
}
|
|
98
|
+
const hasClient = await this.fs.exists(this.fs.join(root, "index.html"));
|
|
94
99
|
|
|
95
100
|
// Build client (precompress always enabled)
|
|
96
101
|
if (hasClient) {
|
|
@@ -110,12 +115,26 @@ export class BuildCommand {
|
|
|
110
115
|
await run({
|
|
111
116
|
name: "vite build server",
|
|
112
117
|
handler: async () => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
const clientIndexPath = `${distDir}/${clientDir}/index.html`;
|
|
119
|
+
const clientBuilt = await this.fs.exists(clientIndexPath);
|
|
120
|
+
|
|
121
|
+
const conditions: string[] = [];
|
|
122
|
+
|
|
123
|
+
// bun:
|
|
124
|
+
// - alepha
|
|
125
|
+
// - react-dom
|
|
126
|
+
|
|
127
|
+
if (flags.bun) {
|
|
128
|
+
conditions.push("bun");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// workerd:
|
|
132
|
+
// - react-dom
|
|
133
|
+
// - postgres
|
|
134
|
+
|
|
135
|
+
// TODO: investigate if we have more conditions like 'edge' to add here
|
|
136
|
+
if (options.cloudflare) {
|
|
137
|
+
conditions.push("workerd");
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
await buildServer({
|
|
@@ -124,11 +143,12 @@ export class BuildCommand {
|
|
|
124
143
|
distDir,
|
|
125
144
|
clientDir: clientBuilt ? clientDir : undefined,
|
|
126
145
|
stats,
|
|
146
|
+
conditions,
|
|
127
147
|
});
|
|
128
148
|
|
|
129
149
|
// Server will handle index.html if both client & server are built
|
|
130
150
|
if (clientBuilt) {
|
|
131
|
-
await
|
|
151
|
+
await this.fs.rm(clientIndexPath);
|
|
132
152
|
}
|
|
133
153
|
},
|
|
134
154
|
});
|
|
@@ -148,7 +168,7 @@ export class BuildCommand {
|
|
|
148
168
|
await run({
|
|
149
169
|
name: "add sitemap",
|
|
150
170
|
handler: async () => {
|
|
151
|
-
await writeFile(
|
|
171
|
+
await this.fs.writeFile(
|
|
152
172
|
`${distDir}/${clientDir}/sitemap.xml`,
|
|
153
173
|
await generateSitemap({
|
|
154
174
|
entry: `${distDir}/index.js`,
|
package/src/cli/commands/db.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { $inject, AlephaError, t } from "alepha";
|
|
4
2
|
import { $command } from "alepha/command";
|
|
3
|
+
import { FileSystemProvider } from "alepha/file";
|
|
5
4
|
import { $logger } from "alepha/logger";
|
|
6
5
|
import type {
|
|
7
6
|
DatabaseProvider,
|
|
@@ -27,6 +26,7 @@ const drizzleCommandFlags = t.object({
|
|
|
27
26
|
|
|
28
27
|
export class DbCommand {
|
|
29
28
|
protected readonly log = $logger();
|
|
29
|
+
protected readonly fs = $inject(FileSystemProvider);
|
|
30
30
|
protected readonly utils = $inject(AlephaCliUtils);
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -66,26 +66,23 @@ export class DbCommand {
|
|
|
66
66
|
|
|
67
67
|
accepted.add(providerName);
|
|
68
68
|
|
|
69
|
-
const migrationDir = join(rootDir, "migrations", providerName);
|
|
69
|
+
const migrationDir = this.fs.join(rootDir, "migrations", providerName);
|
|
70
70
|
|
|
71
|
-
const
|
|
72
|
-
`${migrationDir}/meta/_journal.json
|
|
73
|
-
|
|
74
|
-
).catch(() => null);
|
|
71
|
+
const journalBuffer = await this.fs
|
|
72
|
+
.readFile(`${migrationDir}/meta/_journal.json`)
|
|
73
|
+
.catch(() => null);
|
|
75
74
|
|
|
76
|
-
if (!
|
|
75
|
+
if (!journalBuffer) {
|
|
77
76
|
this.log.info("No migration journal found.");
|
|
78
77
|
return;
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
const journal = JSON.parse(
|
|
80
|
+
const journal = JSON.parse(journalBuffer.toString("utf-8"));
|
|
82
81
|
const lastMigration = journal.entries[journal.entries.length - 1];
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
`${migrationDir}/meta/${String(lastMigration.idx).padStart(4, "0")}_snapshot.json`,
|
|
86
|
-
"utf-8",
|
|
87
|
-
),
|
|
82
|
+
const snapshotBuffer = await this.fs.readFile(
|
|
83
|
+
`${migrationDir}/meta/${String(lastMigration.idx).padStart(4, "0")}_snapshot.json`,
|
|
88
84
|
);
|
|
85
|
+
const lastSnapshot = JSON.parse(snapshotBuffer.toString("utf-8"));
|
|
89
86
|
|
|
90
87
|
const models = drizzleKitProvider.getModels(provider);
|
|
91
88
|
const kit = drizzleKitProvider.importDrizzleKit();
|
|
@@ -295,6 +292,10 @@ export class DbCommand {
|
|
|
295
292
|
const providerName = provider.name;
|
|
296
293
|
const dialect = provider.dialect;
|
|
297
294
|
|
|
295
|
+
if (providerName === "") {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
|
|
298
299
|
if (accepted.has(providerName)) {
|
|
299
300
|
continue;
|
|
300
301
|
}
|
|
@@ -316,6 +317,7 @@ export class DbCommand {
|
|
|
316
317
|
provider,
|
|
317
318
|
providerName,
|
|
318
319
|
providerUrl: provider.url,
|
|
320
|
+
providerDriver: provider.driver,
|
|
319
321
|
dialect,
|
|
320
322
|
entry,
|
|
321
323
|
rootDir,
|
|
@@ -341,6 +343,7 @@ export class DbCommand {
|
|
|
341
343
|
provider: DatabaseProvider;
|
|
342
344
|
providerName: string;
|
|
343
345
|
providerUrl: string;
|
|
346
|
+
providerDriver: string;
|
|
344
347
|
dialect: string;
|
|
345
348
|
entry: string;
|
|
346
349
|
rootDir: string;
|
|
@@ -371,16 +374,16 @@ export class DbCommand {
|
|
|
371
374
|
config.schemaFilter = options.provider.schema;
|
|
372
375
|
}
|
|
373
376
|
|
|
374
|
-
if (options.
|
|
377
|
+
if (options.providerDriver === "d1") {
|
|
375
378
|
config.driver = "d1-http";
|
|
376
379
|
}
|
|
377
380
|
|
|
378
|
-
if (options.
|
|
381
|
+
if (options.providerDriver === "pglite") {
|
|
379
382
|
config.driver = "pglite";
|
|
380
383
|
}
|
|
381
384
|
|
|
382
385
|
if (options.dialect === "sqlite") {
|
|
383
|
-
if (options.
|
|
386
|
+
if (options.providerDriver === "d1") {
|
|
384
387
|
const token = process.env.CLOUDFLARE_API_TOKEN;
|
|
385
388
|
if (!token) {
|
|
386
389
|
throw new AlephaError(
|
|
@@ -421,7 +424,7 @@ export class DbCommand {
|
|
|
421
424
|
} else {
|
|
422
425
|
let url = options.providerUrl;
|
|
423
426
|
url = url.replace("sqlite://", "").replace("file://", "");
|
|
424
|
-
url = join(options.rootDir, url);
|
|
427
|
+
url = this.fs.join(options.rootDir, url);
|
|
425
428
|
|
|
426
429
|
config.dbCredentials = {
|
|
427
430
|
url,
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { $inject, AlephaError, t } from "alepha";
|
|
3
2
|
import { $command } from "alepha/command";
|
|
3
|
+
import { FileSystemProvider } from "alepha/file";
|
|
4
4
|
import { $logger } from "alepha/logger";
|
|
5
5
|
import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
|
|
6
|
+
import { PackageManagerUtils } from "../services/PackageManagerUtils.ts";
|
|
6
7
|
|
|
7
8
|
export class DeployCommand {
|
|
8
9
|
protected readonly log = $logger();
|
|
10
|
+
protected readonly fs = $inject(FileSystemProvider);
|
|
9
11
|
protected readonly utils = $inject(AlephaCliUtils);
|
|
12
|
+
protected readonly pm = $inject(PackageManagerUtils);
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* Deploy the project to a hosting platform (e.g., Vercel, Cloudflare, Surge)
|
|
@@ -79,7 +82,10 @@ export class DeployCommand {
|
|
|
79
82
|
this.log.debug("Running database migrations before deployment...");
|
|
80
83
|
await this.utils.exec(`alepha db migrate --mode=${mode}`);
|
|
81
84
|
}
|
|
82
|
-
await this.
|
|
85
|
+
await this.pm.ensureDependency(root, "vercel", {
|
|
86
|
+
dev: true,
|
|
87
|
+
exec: (cmd, opts) => this.utils.exec(cmd, opts),
|
|
88
|
+
});
|
|
83
89
|
const command =
|
|
84
90
|
`vercel . --cwd=dist ${mode === "production" ? "--prod" : ""}`.trim();
|
|
85
91
|
this.log.debug(`Deploying to Vercel with command: ${command}`);
|
|
@@ -93,7 +99,10 @@ export class DeployCommand {
|
|
|
93
99
|
this.log.debug("Running database migrations before deployment...");
|
|
94
100
|
await this.utils.exec(`alepha db migrate --mode=${mode}`);
|
|
95
101
|
}
|
|
96
|
-
await this.
|
|
102
|
+
await this.pm.ensureDependency(root, "wrangler", {
|
|
103
|
+
dev: true,
|
|
104
|
+
exec: (cmd, opts) => this.utils.exec(cmd, opts),
|
|
105
|
+
});
|
|
97
106
|
const command =
|
|
98
107
|
`wrangler deploy ${mode === "production" ? "" : "--env preview"} --config=dist/wrangler.jsonc`.trim();
|
|
99
108
|
this.log.info(`Deploying to Cloudflare with command: ${command}`);
|
|
@@ -103,8 +112,11 @@ export class DeployCommand {
|
|
|
103
112
|
|
|
104
113
|
// Surge deployment
|
|
105
114
|
if (await this.utils.exists(root, "dist/public/404.html")) {
|
|
106
|
-
await this.
|
|
107
|
-
|
|
115
|
+
await this.pm.ensureDependency(root, "surge", {
|
|
116
|
+
dev: true,
|
|
117
|
+
exec: (cmd, opts) => this.utils.exec(cmd, opts),
|
|
118
|
+
});
|
|
119
|
+
const distPath = this.fs.join(root, "dist/public");
|
|
108
120
|
this.log.debug(`Deploying to Surge from directory: ${distPath}`);
|
|
109
121
|
await this.utils.exec(`surge ${distPath}`);
|
|
110
122
|
return;
|