alepha 0.20.2 → 0.20.4
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 +0 -1
- package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
- package/assets/swagger-ui/swagger-ui.css +1 -1
- package/dist/api/audits/index.browser.js +49 -0
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.js +49 -0
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +2 -61
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +4 -4
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +1 -10
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/parameters/index.browser.js +37 -0
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +12 -68
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +57 -4
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/users/index.browser.js +6 -0
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +148 -227
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +60 -14
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +2 -1
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/bucket/index.d.ts +77 -107
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +153 -5
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +12 -2
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +26 -0
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +11 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +11 -1
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +7 -5
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js +2 -3
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +637 -11660
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +707 -532
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +4 -8
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js +20 -16
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +51 -77
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +65 -15
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +10 -13
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js +30 -12
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.js +1 -1
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +27 -3
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +8 -11
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +27 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +27 -3
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +27 -3
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.d.ts +69 -10
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +135 -13
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/smtp/index.js +130 -16
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +30 -2
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +35 -12
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.js +32 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +238 -31
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +198 -67
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +2 -362
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +18 -409
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +41 -194
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +27 -422
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +17 -20
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +1 -5
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +17 -20
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/react/core/index.d.ts +102 -1
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +65 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +6 -0
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +7 -7
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +7 -1
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +6 -0
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.js +22 -17
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +98 -4
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +58 -5
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +122 -6
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/testing/{chunk-DBEY4PJZ.js → chunk-6Ep1yQYe.js} +1 -1
- package/dist/react/testing/index.js +1 -1
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +195 -1
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js +64 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/react/websocket/index.js.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +1 -1
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +2 -2
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +24 -10
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js +10 -3
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +1 -4
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +47 -9
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.js +19 -1
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +4 -5
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/websocket/index.browser.js +32 -5
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +3 -1
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +42 -6
- package/dist/websocket/index.js.map +1 -1
- package/package.json +685 -274
- package/src/api/files/__tests__/FileController.spec.ts +1 -1
- package/src/api/jobs/__tests__/$job.spec.ts +5 -1
- package/src/api/parameters/services/ParameterProvider.ts +21 -4
- package/src/api/users/__tests__/SessionService.spec.ts +99 -0
- package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
- package/src/api/users/entities/sessions.ts +6 -0
- package/src/api/users/jobs/UserJobs.ts +44 -17
- package/src/api/users/providers/RealmProvider.ts +4 -0
- package/src/api/users/schemas/userQuerySchema.ts +0 -1
- package/src/api/users/services/SessionService.ts +27 -0
- package/src/api/users/services/UserService.ts +1 -5
- package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
- package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
- package/src/api/verifications/services/VerificationService.ts +1 -0
- package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
- package/src/bucket/index.ts +19 -2
- package/src/bucket/primitives/$bucket.ts +9 -1
- package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
- package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
- package/src/cache/core/index.ts +29 -0
- package/src/cache/core/primitives/$cache.ts +14 -1
- package/src/cli/config/defineConfig.ts +13 -15
- package/src/cli/core/__tests__/init.spec.ts +214 -7
- package/src/cli/core/commands/init.ts +12 -0
- package/src/cli/core/services/PackageManagerUtils.ts +23 -6
- package/src/cli/core/services/ProjectScaffolder.ts +315 -33
- package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
- package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
- package/src/cli/core/tasks/BuildServerTask.ts +8 -0
- package/src/cli/core/templates/agentMd.ts +2 -10
- package/src/cli/core/templates/apiIndexTs.ts +23 -1
- package/src/cli/core/templates/componentsJsonTs.ts +39 -0
- package/src/cli/core/templates/mainCss.ts +1 -0
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
- package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
- package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
- package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
- package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
- package/src/cli/core/templates/webAppRouterTs.ts +104 -1
- package/src/cli/core/templates/webIndexTs.ts +23 -1
- package/src/cli/devtools/index.ts +12 -26
- package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
- package/src/cli/platform/index.ts +15 -24
- package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
- package/src/cli/vendor/index.ts +14 -23
- package/src/command/providers/CliProvider.ts +1 -1
- package/src/core/Alepha.ts +11 -1
- package/src/core/helpers/ref.ts +18 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/interfaces/Service.ts +3 -1
- package/src/core/providers/SchemaValidator.ts +9 -1
- package/src/core/providers/TypeProvider.ts +2 -3
- package/src/datetime/REFACTORING.md +118 -0
- package/src/datetime/providers/DateTimeProvider.ts +203 -24
- package/src/lock/core/index.ts +31 -0
- package/src/lock/core/primitives/$lock.ts +14 -1
- package/src/logger/services/Logger.ts +1 -1
- package/src/mcp/__tests__/$resource.spec.ts +1 -1
- package/src/mcp/__tests__/$tool.spec.ts +1 -1
- package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
- package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
- package/src/mcp/helpers/jsonrpc.ts +26 -1
- package/src/mcp/index.ts +10 -5
- package/src/mcp/interfaces/McpTypes.ts +83 -6
- package/src/mcp/primitives/$prompt.ts +18 -1
- package/src/mcp/primitives/$resource.ts +18 -1
- package/src/mcp/primitives/$tool.ts +83 -7
- package/src/mcp/providers/McpServerProvider.ts +74 -16
- package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
- package/src/orm/REFACTORING.md +330 -0
- package/src/orm/__tests__/$repository-tests.ts +1 -0
- package/src/orm/__tests__/orm-next-tests.ts +2 -67
- package/src/orm/__tests__/orm-next.spec.ts +0 -21
- package/src/orm/core/index.shared.ts +0 -2
- package/src/orm/core/index.ts +1 -2
- package/src/orm/core/primitives/$repository.ts +3 -6
- package/src/orm/core/primitives/$transactional.ts +11 -0
- package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
- package/src/orm/core/schemas/updateSchema.ts +1 -1
- package/src/orm/core/services/ModelBuilder.ts +1 -13
- package/src/orm/core/services/PgRelationManager.ts +4 -2
- package/src/orm/core/services/Repository.ts +1 -42
- package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
- package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
- package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
- package/src/react/core/hooks/useQuery.ts +153 -0
- package/src/react/core/index.ts +1 -0
- package/src/react/form/services/FormModel.ts +15 -6
- package/src/react/form/services/parseField.ts +8 -0
- package/src/react/i18n/providers/I18nProvider.ts +8 -2
- package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
- package/src/react/router/__tests__/$page.spec.tsx +0 -16
- package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
- package/src/react/router/__tests__/ssr.spec.tsx +339 -0
- package/src/react/router/primitives/$page.ts +28 -4
- package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
- package/src/react/router/providers/ReactPageProvider.ts +27 -9
- package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
- package/src/react/router/providers/ReactServerProvider.ts +1 -0
- package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
- package/src/react/ui/index.ts +6 -0
- package/src/react/ui/services/SchemaControl.ts +209 -0
- package/src/scheduler/providers/CronProvider.ts +1 -1
- package/src/security/primitives/$basicAuth.ts +1 -1
- package/src/security/primitives/$issuer.ts +6 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
- package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
- package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
- package/src/server/core/errors/ValidationError.ts +13 -1
- package/src/server/core/interfaces/ServerRequest.ts +1 -0
- package/src/server/core/primitives/$action.ts +16 -5
- package/src/server/core/providers/ServerProvider.ts +1 -1
- package/src/server/core/providers/ServerRouterProvider.ts +28 -6
- package/src/server/core/services/HttpClient.ts +1 -1
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +6 -8
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
- package/src/websocket/services/WebSocketClient.ts +11 -5
- package/src/mcp/transports/SseMcpTransport.ts +0 -182
- package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
- package/src/orm/core/helpers/parseQueryString.ts +0 -502
- package/src/orm/core/primitives/$view.ts +0 -88
package/dist/cli/core/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
9
9
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
10
|
+
import pkg from "alepha/package.json" with { type: "json" };
|
|
10
11
|
import { analyzer } from "vite-bundle-analyzer";
|
|
11
12
|
import { KV_DEFAULT_BINDING } from "alepha/cache";
|
|
12
13
|
import { QUEUE_DEFAULT_BINDING } from "alepha/queue";
|
|
@@ -37,7 +38,20 @@ const buildOptions = $atom({
|
|
|
37
38
|
name: "alepha.cli.build.options",
|
|
38
39
|
description: "Build configuration options",
|
|
39
40
|
schema: t.object({
|
|
41
|
+
/**
|
|
42
|
+
* Generate build stats report.
|
|
43
|
+
*
|
|
44
|
+
* - `true` - Generate a static HTML report
|
|
45
|
+
* - `"json"` - Generate a JSON report
|
|
46
|
+
*/
|
|
40
47
|
stats: t.optional(t.union([t.boolean(), t.enum(["json"])])),
|
|
48
|
+
/**
|
|
49
|
+
* Deployment target for the build output.
|
|
50
|
+
*
|
|
51
|
+
* - `docker` - Generate Dockerfile for containerized deployment
|
|
52
|
+
* - `vercel` - Generate Vercel deployment configuration (forces node runtime)
|
|
53
|
+
* - `cloudflare` - Generate Cloudflare Workers configuration (forces workerd runtime)
|
|
54
|
+
*/
|
|
41
55
|
target: t.optional(t.enum([
|
|
42
56
|
"bare",
|
|
43
57
|
"docker",
|
|
@@ -45,15 +59,45 @@ const buildOptions = $atom({
|
|
|
45
59
|
"cloudflare",
|
|
46
60
|
"static"
|
|
47
61
|
])),
|
|
62
|
+
/**
|
|
63
|
+
* JavaScript runtime for the build output.
|
|
64
|
+
*
|
|
65
|
+
* - `node` - Node.js runtime (default)
|
|
66
|
+
* - `bun` - Bun runtime (uses bun export conditions)
|
|
67
|
+
* - `workerd` - Cloudflare Workers runtime (auto-set with cloudflare target)
|
|
68
|
+
*
|
|
69
|
+
* Note: Some targets force a specific runtime:
|
|
70
|
+
* - `cloudflare` always uses `workerd`
|
|
71
|
+
* - `vercel` always uses `node`
|
|
72
|
+
*/
|
|
48
73
|
runtime: t.optional(t.enum([
|
|
49
74
|
"node",
|
|
50
75
|
"bun",
|
|
51
76
|
"workerd"
|
|
52
77
|
])),
|
|
78
|
+
/**
|
|
79
|
+
* Output directory configuration.
|
|
80
|
+
*/
|
|
53
81
|
output: t.optional(t.object({
|
|
82
|
+
/**
|
|
83
|
+
* Root dist directory.
|
|
84
|
+
*
|
|
85
|
+
* @default "dist"
|
|
86
|
+
*/
|
|
54
87
|
dist: t.optional(t.string({ default: "dist" })),
|
|
88
|
+
/**
|
|
89
|
+
* Public/client subdirectory.
|
|
90
|
+
*
|
|
91
|
+
* @default "public"
|
|
92
|
+
*/
|
|
55
93
|
public: t.optional(t.string({ default: "public" }))
|
|
56
94
|
})),
|
|
95
|
+
/**
|
|
96
|
+
* Vercel-specific deployment configuration.
|
|
97
|
+
*
|
|
98
|
+
* Note: Set `target: "vercel"` to enable Vercel deployment.
|
|
99
|
+
* This object is only for additional configuration.
|
|
100
|
+
*/
|
|
57
101
|
vercel: t.optional(t.object({
|
|
58
102
|
projectName: t.optional(t.string()),
|
|
59
103
|
orgId: t.optional(t.string()),
|
|
@@ -63,31 +107,143 @@ const buildOptions = $atom({
|
|
|
63
107
|
schedule: t.string()
|
|
64
108
|
}))) }))
|
|
65
109
|
})),
|
|
110
|
+
/**
|
|
111
|
+
* Cloudflare-specific deployment configuration.
|
|
112
|
+
*
|
|
113
|
+
* Note: Set `target: "cloudflare"` to enable Cloudflare deployment.
|
|
114
|
+
* This object is only for additional configuration.
|
|
115
|
+
*/
|
|
66
116
|
cloudflare: t.optional(t.object({ config: t.optional(t.json()) })),
|
|
117
|
+
/**
|
|
118
|
+
* Docker-specific deployment configuration.
|
|
119
|
+
*
|
|
120
|
+
* Note: Set `target: "docker"` to enable Docker deployment.
|
|
121
|
+
* This object is only for additional configuration.
|
|
122
|
+
*/
|
|
67
123
|
docker: t.optional(t.object({
|
|
124
|
+
/**
|
|
125
|
+
* Base image for the Dockerfile (FROM instruction).
|
|
126
|
+
*
|
|
127
|
+
* @default "node:24-alpine" for node runtime
|
|
128
|
+
* @default "oven/bun:alpine" for bun runtime
|
|
129
|
+
*/
|
|
68
130
|
from: t.optional(t.string()),
|
|
131
|
+
/**
|
|
132
|
+
* Command to run in the Docker container.
|
|
133
|
+
*
|
|
134
|
+
* @default "node" for node runtime
|
|
135
|
+
* @default "bun" for bun runtime
|
|
136
|
+
*/
|
|
69
137
|
command: t.optional(t.string()),
|
|
138
|
+
/**
|
|
139
|
+
* Docker build options (used when --image flag is passed).
|
|
140
|
+
*/
|
|
70
141
|
image: t.optional(t.object({
|
|
142
|
+
/**
|
|
143
|
+
* Default image tag (name without version).
|
|
144
|
+
*
|
|
145
|
+
* Used when --image is provided without a full override:
|
|
146
|
+
* - `--image` → `tag:latest`
|
|
147
|
+
* - `--image=1.3.4` → `tag:1.3.4`
|
|
148
|
+
* - `--image=other/img:v1` → `other/img:v1` (full override)
|
|
149
|
+
*
|
|
150
|
+
* @example "myproject/myapp"
|
|
151
|
+
* @example "ghcr.io/myorg/myapp"
|
|
152
|
+
*/
|
|
71
153
|
tag: t.string(),
|
|
154
|
+
/**
|
|
155
|
+
* Additional arguments to pass to `docker build`.
|
|
156
|
+
*
|
|
157
|
+
* @example '--platform linux/amd64 --no-cache'
|
|
158
|
+
*/
|
|
72
159
|
args: t.optional(t.string()),
|
|
160
|
+
/**
|
|
161
|
+
* Auto-add OCI standard labels (revision, created, version).
|
|
162
|
+
*
|
|
163
|
+
* Adds:
|
|
164
|
+
* - org.opencontainers.image.revision (git commit SHA)
|
|
165
|
+
* - org.opencontainers.image.created (build timestamp)
|
|
166
|
+
* - org.opencontainers.image.version (from image tag)
|
|
167
|
+
*/
|
|
73
168
|
oci: t.optional(t.boolean())
|
|
74
169
|
}))
|
|
75
170
|
})),
|
|
76
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Static site deployment configuration.
|
|
173
|
+
*
|
|
174
|
+
* Note: Set `target: "static"` to enable static site generation.
|
|
175
|
+
*/
|
|
176
|
+
static: t.optional(t.object({
|
|
177
|
+
/**
|
|
178
|
+
* Surge domain for deployment.
|
|
179
|
+
*
|
|
180
|
+
* If set, a CNAME file is written to dist/public/.
|
|
181
|
+
* If not set, a domain is auto-generated from package.json name.
|
|
182
|
+
*
|
|
183
|
+
* @example "my-app.surge.sh"
|
|
184
|
+
* @example "my-custom-domain.com"
|
|
185
|
+
*/
|
|
186
|
+
domain: t.optional(t.string()) })),
|
|
187
|
+
/**
|
|
188
|
+
* PWA (Progressive Web App) configuration.
|
|
189
|
+
*
|
|
190
|
+
* Generates a web app manifest and enables installability.
|
|
191
|
+
* Requires a client-side bundle (React).
|
|
192
|
+
*/
|
|
77
193
|
pwa: t.optional(t.object({
|
|
194
|
+
/**
|
|
195
|
+
* Full application name displayed on the splash screen
|
|
196
|
+
* and in the OS app switcher.
|
|
197
|
+
*/
|
|
78
198
|
name: t.string(),
|
|
199
|
+
/**
|
|
200
|
+
* Short name displayed on the home screen icon.
|
|
201
|
+
* Falls back to `name` if omitted.
|
|
202
|
+
*/
|
|
79
203
|
shortName: t.optional(t.string()),
|
|
204
|
+
/**
|
|
205
|
+
* Theme color used for the browser toolbar and OS chrome.
|
|
206
|
+
*
|
|
207
|
+
* @default "#ffffff"
|
|
208
|
+
*/
|
|
80
209
|
themeColor: t.optional(t.string()),
|
|
210
|
+
/**
|
|
211
|
+
* Background color for the splash screen.
|
|
212
|
+
*
|
|
213
|
+
* @default "#ffffff"
|
|
214
|
+
*/
|
|
81
215
|
backgroundColor: t.optional(t.string()),
|
|
216
|
+
/**
|
|
217
|
+
* Display mode for the installed PWA.
|
|
218
|
+
*
|
|
219
|
+
* - `standalone` - Looks like a native app (default)
|
|
220
|
+
* - `fullscreen` - Uses entire screen (games, immersive)
|
|
221
|
+
* - `minimal-ui` - Like standalone with minimal browser UI
|
|
222
|
+
* - `browser` - Standard browser tab
|
|
223
|
+
*
|
|
224
|
+
* @default "standalone"
|
|
225
|
+
*/
|
|
82
226
|
display: t.optional(t.enum([
|
|
83
227
|
"standalone",
|
|
84
228
|
"fullscreen",
|
|
85
229
|
"minimal-ui",
|
|
86
230
|
"browser"
|
|
87
231
|
])),
|
|
232
|
+
/**
|
|
233
|
+
* Enable offline support via service worker.
|
|
234
|
+
*
|
|
235
|
+
* TODO: Not yet implemented.
|
|
236
|
+
*/
|
|
88
237
|
offline: t.optional(t.boolean())
|
|
89
238
|
})),
|
|
90
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Sitemap generation configuration.
|
|
241
|
+
*/
|
|
242
|
+
sitemap: t.optional(t.object({
|
|
243
|
+
/**
|
|
244
|
+
* Base URL for sitemap entries.
|
|
245
|
+
*/
|
|
246
|
+
hostname: t.string() }))
|
|
91
247
|
}),
|
|
92
248
|
default: {}
|
|
93
249
|
});
|
|
@@ -102,7 +258,11 @@ const buildOptions = $atom({
|
|
|
102
258
|
const devOptions = $atom({
|
|
103
259
|
name: "alepha.cli.dev.options",
|
|
104
260
|
description: "Dev configuration options",
|
|
105
|
-
schema: t.object({
|
|
261
|
+
schema: t.object({
|
|
262
|
+
/**
|
|
263
|
+
* Disable Vite React plugin.
|
|
264
|
+
*/
|
|
265
|
+
noViteReactPlugin: t.optional(t.boolean({ default: false })) }),
|
|
106
266
|
default: {}
|
|
107
267
|
});
|
|
108
268
|
//#endregion
|
|
@@ -597,500 +757,9 @@ var AlephaCliUtils = class {
|
|
|
597
757
|
}
|
|
598
758
|
};
|
|
599
759
|
//#endregion
|
|
600
|
-
//#region ../../package.json
|
|
601
|
-
var version$1 = "0.20.2";
|
|
602
|
-
//#endregion
|
|
603
760
|
//#region ../../src/cli/core/alephaPackageJson.ts
|
|
604
|
-
const alephaPackageJson =
|
|
605
|
-
|
|
606
|
-
description: "Easy-to-use modern TypeScript framework for building many kind of applications.",
|
|
607
|
-
author: "Feunard",
|
|
608
|
-
version: version$1,
|
|
609
|
-
type: "module",
|
|
610
|
-
engines: { "node": ">=22.0.0" },
|
|
611
|
-
license: "MIT",
|
|
612
|
-
bin: "./src/bin/index.ts",
|
|
613
|
-
main: "./src/core/index.ts",
|
|
614
|
-
types: "./src/core/index.ts",
|
|
615
|
-
files: [
|
|
616
|
-
"dist",
|
|
617
|
-
"src",
|
|
618
|
-
"assets",
|
|
619
|
-
"tsconfig.base.json",
|
|
620
|
-
"AGENTS.md",
|
|
621
|
-
"CLAUDE.md"
|
|
622
|
-
],
|
|
623
|
-
dependencies: {
|
|
624
|
-
"@redis/client": "^5.12.1",
|
|
625
|
-
"@vitejs/plugin-react": "^6.0.1",
|
|
626
|
-
"dayjs": "^1.11.20",
|
|
627
|
-
"drizzle-orm": "^0.45.2",
|
|
628
|
-
"postgres": "^3.4.9",
|
|
629
|
-
"tsx": "^4.21.0",
|
|
630
|
-
"typebox": "^1.1.23",
|
|
631
|
-
"typescript": "^6.0.2",
|
|
632
|
-
"vite-bundle-analyzer": "^1.3.7",
|
|
633
|
-
"ws": "^8.20.0"
|
|
634
|
-
},
|
|
635
|
-
devDependencies: {
|
|
636
|
-
"@biomejs/biome": "^2.4.12",
|
|
637
|
-
"@electric-sql/pglite": "^0.4.4",
|
|
638
|
-
"@faker-js/faker": "^10.4.0",
|
|
639
|
-
"@testing-library/dom": "^10.4.1",
|
|
640
|
-
"@testing-library/react": "^16.3.2",
|
|
641
|
-
"@types/bun": "^1.3.12",
|
|
642
|
-
"@types/node": "^25.6.0",
|
|
643
|
-
"@types/nodemailer": "^8.0.0",
|
|
644
|
-
"@types/react": "^19.2.14",
|
|
645
|
-
"@types/react-dom": "^19.2.3",
|
|
646
|
-
"@types/ws": "^8.18.1",
|
|
647
|
-
"cron-schedule": "^6.0.0",
|
|
648
|
-
"drizzle-kit": "^0.31.10",
|
|
649
|
-
"jose": "^6.2.2",
|
|
650
|
-
"jsdom": "^29.0.2",
|
|
651
|
-
"nodemailer": "^8.0.5",
|
|
652
|
-
"openid-client": "^6.8.3",
|
|
653
|
-
"prom-client": "^15.1.3",
|
|
654
|
-
"react": "^19.2.5",
|
|
655
|
-
"react-dom": "^19.2.5",
|
|
656
|
-
"swagger-ui-dist": "^5.32.4",
|
|
657
|
-
"tsdown": "^0.21.8",
|
|
658
|
-
"vite": "^8.0.8",
|
|
659
|
-
"vitest": "^4.1.4"
|
|
660
|
-
},
|
|
661
|
-
peerDependencies: {
|
|
662
|
-
"react": "^19",
|
|
663
|
-
"react-dom": "^19"
|
|
664
|
-
},
|
|
665
|
-
peerDependenciesMeta: {
|
|
666
|
-
"react": { "optional": true },
|
|
667
|
-
"react-dom": { "optional": true }
|
|
668
|
-
},
|
|
669
|
-
scarfSettings: { "enabled": false },
|
|
670
|
-
scripts: {
|
|
671
|
-
"lint": "biome check --fix && yarn build:check",
|
|
672
|
-
"typecheck": "tsc --noEmit",
|
|
673
|
-
"test": "vitest run",
|
|
674
|
-
"build": "node scripts/build.ts",
|
|
675
|
-
"build:check": "yarn build --check",
|
|
676
|
-
"copy": "node scripts/copy-swagger.ts"
|
|
677
|
-
},
|
|
678
|
-
homepage: "https://github.com/feunard/alepha",
|
|
679
|
-
repository: {
|
|
680
|
-
"type": "git",
|
|
681
|
-
"url": "git+https://github.com/feunard/alepha.git"
|
|
682
|
-
},
|
|
683
|
-
keywords: [
|
|
684
|
-
"alepha",
|
|
685
|
-
"aleph",
|
|
686
|
-
"framework",
|
|
687
|
-
"serverless",
|
|
688
|
-
"react",
|
|
689
|
-
"api"
|
|
690
|
-
],
|
|
691
|
-
exports: {
|
|
692
|
-
"./api/audits": {
|
|
693
|
-
"types": "./src/api/audits/index.ts",
|
|
694
|
-
"react-native": "./src/api/audits/index.browser.ts",
|
|
695
|
-
"browser": "./src/api/audits/index.browser.ts",
|
|
696
|
-
"import": "./src/api/audits/index.ts",
|
|
697
|
-
"default": "./src/api/audits/index.ts"
|
|
698
|
-
},
|
|
699
|
-
"./api/files": {
|
|
700
|
-
"types": "./src/api/files/index.ts",
|
|
701
|
-
"react-native": "./src/api/files/index.browser.ts",
|
|
702
|
-
"browser": "./src/api/files/index.browser.ts",
|
|
703
|
-
"import": "./src/api/files/index.ts",
|
|
704
|
-
"default": "./src/api/files/index.ts"
|
|
705
|
-
},
|
|
706
|
-
"./api/jobs": {
|
|
707
|
-
"types": "./src/api/jobs/index.ts",
|
|
708
|
-
"react-native": "./src/api/jobs/index.browser.ts",
|
|
709
|
-
"browser": "./src/api/jobs/index.browser.ts",
|
|
710
|
-
"import": "./src/api/jobs/index.ts",
|
|
711
|
-
"default": "./src/api/jobs/index.ts"
|
|
712
|
-
},
|
|
713
|
-
"./api/keys": {
|
|
714
|
-
"types": "./src/api/keys/index.ts",
|
|
715
|
-
"import": "./src/api/keys/index.ts",
|
|
716
|
-
"default": "./src/api/keys/index.ts"
|
|
717
|
-
},
|
|
718
|
-
"./api/notifications": {
|
|
719
|
-
"types": "./src/api/notifications/index.ts",
|
|
720
|
-
"react-native": "./src/api/notifications/index.browser.ts",
|
|
721
|
-
"browser": "./src/api/notifications/index.browser.ts",
|
|
722
|
-
"import": "./src/api/notifications/index.ts",
|
|
723
|
-
"default": "./src/api/notifications/index.ts"
|
|
724
|
-
},
|
|
725
|
-
"./api/organizations": {
|
|
726
|
-
"types": "./src/api/organizations/index.ts",
|
|
727
|
-
"react-native": "./src/api/organizations/index.browser.ts",
|
|
728
|
-
"browser": "./src/api/organizations/index.browser.ts",
|
|
729
|
-
"import": "./src/api/organizations/index.ts",
|
|
730
|
-
"default": "./src/api/organizations/index.ts"
|
|
731
|
-
},
|
|
732
|
-
"./api/parameters": {
|
|
733
|
-
"types": "./src/api/parameters/index.ts",
|
|
734
|
-
"react-native": "./src/api/parameters/index.browser.ts",
|
|
735
|
-
"browser": "./src/api/parameters/index.browser.ts",
|
|
736
|
-
"import": "./src/api/parameters/index.ts",
|
|
737
|
-
"default": "./src/api/parameters/index.ts"
|
|
738
|
-
},
|
|
739
|
-
"./api/payments": {
|
|
740
|
-
"types": "./src/api/payments/index.ts",
|
|
741
|
-
"import": "./src/api/payments/index.ts",
|
|
742
|
-
"default": "./src/api/payments/index.ts"
|
|
743
|
-
},
|
|
744
|
-
"./api/users": {
|
|
745
|
-
"types": "./src/api/users/index.ts",
|
|
746
|
-
"react-native": "./src/api/users/index.browser.ts",
|
|
747
|
-
"browser": "./src/api/users/index.browser.ts",
|
|
748
|
-
"import": "./src/api/users/index.ts",
|
|
749
|
-
"default": "./src/api/users/index.ts"
|
|
750
|
-
},
|
|
751
|
-
"./api/verifications": {
|
|
752
|
-
"types": "./src/api/verifications/index.ts",
|
|
753
|
-
"react-native": "./src/api/verifications/index.browser.ts",
|
|
754
|
-
"browser": "./src/api/verifications/index.browser.ts",
|
|
755
|
-
"import": "./src/api/verifications/index.ts",
|
|
756
|
-
"default": "./src/api/verifications/index.ts"
|
|
757
|
-
},
|
|
758
|
-
"./batch": {
|
|
759
|
-
"types": "./src/batch/index.ts",
|
|
760
|
-
"import": "./src/batch/index.ts",
|
|
761
|
-
"default": "./src/batch/index.ts"
|
|
762
|
-
},
|
|
763
|
-
"./bin": {
|
|
764
|
-
"types": "./src/bin/index.ts",
|
|
765
|
-
"import": "./src/bin/index.ts",
|
|
766
|
-
"default": "./src/bin/index.ts"
|
|
767
|
-
},
|
|
768
|
-
"./bucket": {
|
|
769
|
-
"types": "./src/bucket/index.ts",
|
|
770
|
-
"workerd": "./src/bucket/index.workerd.ts",
|
|
771
|
-
"import": "./src/bucket/index.ts",
|
|
772
|
-
"default": "./src/bucket/index.ts"
|
|
773
|
-
},
|
|
774
|
-
"./cache": {
|
|
775
|
-
"types": "./src/cache/core/index.ts",
|
|
776
|
-
"workerd": "./src/cache/core/index.workerd.ts",
|
|
777
|
-
"import": "./src/cache/core/index.ts",
|
|
778
|
-
"default": "./src/cache/core/index.ts"
|
|
779
|
-
},
|
|
780
|
-
"./cache/redis": {
|
|
781
|
-
"types": "./src/cache/redis/index.ts",
|
|
782
|
-
"import": "./src/cache/redis/index.ts",
|
|
783
|
-
"default": "./src/cache/redis/index.ts"
|
|
784
|
-
},
|
|
785
|
-
"./captcha": {
|
|
786
|
-
"types": "./src/captcha/index.ts",
|
|
787
|
-
"import": "./src/captcha/index.ts",
|
|
788
|
-
"default": "./src/captcha/index.ts"
|
|
789
|
-
},
|
|
790
|
-
"./cli/config": {
|
|
791
|
-
"types": "./src/cli/config/index.ts",
|
|
792
|
-
"import": "./src/cli/config/index.ts",
|
|
793
|
-
"default": "./src/cli/config/index.ts"
|
|
794
|
-
},
|
|
795
|
-
"./cli": {
|
|
796
|
-
"types": "./src/cli/core/index.ts",
|
|
797
|
-
"import": "./src/cli/core/index.ts",
|
|
798
|
-
"default": "./src/cli/core/index.ts"
|
|
799
|
-
},
|
|
800
|
-
"./cli/devtools": {
|
|
801
|
-
"types": "./src/cli/devtools/index.ts",
|
|
802
|
-
"import": "./src/cli/devtools/index.ts",
|
|
803
|
-
"default": "./src/cli/devtools/index.ts"
|
|
804
|
-
},
|
|
805
|
-
"./cli/platform": {
|
|
806
|
-
"types": "./src/cli/platform/index.ts",
|
|
807
|
-
"import": "./src/cli/platform/index.ts",
|
|
808
|
-
"default": "./src/cli/platform/index.ts"
|
|
809
|
-
},
|
|
810
|
-
"./cli/vendor": {
|
|
811
|
-
"types": "./src/cli/vendor/index.ts",
|
|
812
|
-
"import": "./src/cli/vendor/index.ts",
|
|
813
|
-
"default": "./src/cli/vendor/index.ts"
|
|
814
|
-
},
|
|
815
|
-
"./command": {
|
|
816
|
-
"types": "./src/command/index.ts",
|
|
817
|
-
"import": "./src/command/index.ts",
|
|
818
|
-
"default": "./src/command/index.ts"
|
|
819
|
-
},
|
|
820
|
-
".": {
|
|
821
|
-
"types": "./src/core/index.ts",
|
|
822
|
-
"react-native": "./src/core/index.native.ts",
|
|
823
|
-
"workerd": "./src/core/index.workerd.ts",
|
|
824
|
-
"browser": "./src/core/index.browser.ts",
|
|
825
|
-
"import": "./src/core/index.ts",
|
|
826
|
-
"default": "./src/core/index.ts"
|
|
827
|
-
},
|
|
828
|
-
"./crypto": {
|
|
829
|
-
"types": "./src/crypto/index.ts",
|
|
830
|
-
"react-native": "./src/crypto/index.browser.ts",
|
|
831
|
-
"browser": "./src/crypto/index.browser.ts",
|
|
832
|
-
"import": "./src/crypto/index.ts",
|
|
833
|
-
"default": "./src/crypto/index.ts"
|
|
834
|
-
},
|
|
835
|
-
"./datetime": {
|
|
836
|
-
"types": "./src/datetime/index.ts",
|
|
837
|
-
"import": "./src/datetime/index.ts",
|
|
838
|
-
"default": "./src/datetime/index.ts"
|
|
839
|
-
},
|
|
840
|
-
"./email/brevo": {
|
|
841
|
-
"types": "./src/email/brevo/index.ts",
|
|
842
|
-
"import": "./src/email/brevo/index.ts",
|
|
843
|
-
"default": "./src/email/brevo/index.ts"
|
|
844
|
-
},
|
|
845
|
-
"./email": {
|
|
846
|
-
"types": "./src/email/core/index.ts",
|
|
847
|
-
"workerd": "./src/email/core/index.workerd.ts",
|
|
848
|
-
"import": "./src/email/core/index.ts",
|
|
849
|
-
"default": "./src/email/core/index.ts"
|
|
850
|
-
},
|
|
851
|
-
"./email/smtp": {
|
|
852
|
-
"types": "./src/email/smtp/index.ts",
|
|
853
|
-
"import": "./src/email/smtp/index.ts",
|
|
854
|
-
"default": "./src/email/smtp/index.ts"
|
|
855
|
-
},
|
|
856
|
-
"./fake": {
|
|
857
|
-
"types": "./src/fake/index.ts",
|
|
858
|
-
"import": "./src/fake/index.ts",
|
|
859
|
-
"default": "./src/fake/index.ts"
|
|
860
|
-
},
|
|
861
|
-
"./lock": {
|
|
862
|
-
"types": "./src/lock/core/index.ts",
|
|
863
|
-
"import": "./src/lock/core/index.ts",
|
|
864
|
-
"default": "./src/lock/core/index.ts"
|
|
865
|
-
},
|
|
866
|
-
"./lock/redis": {
|
|
867
|
-
"types": "./src/lock/redis/index.ts",
|
|
868
|
-
"import": "./src/lock/redis/index.ts",
|
|
869
|
-
"default": "./src/lock/redis/index.ts"
|
|
870
|
-
},
|
|
871
|
-
"./logger": {
|
|
872
|
-
"types": "./src/logger/index.ts",
|
|
873
|
-
"import": "./src/logger/index.ts",
|
|
874
|
-
"default": "./src/logger/index.ts"
|
|
875
|
-
},
|
|
876
|
-
"./mcp": {
|
|
877
|
-
"types": "./src/mcp/index.ts",
|
|
878
|
-
"import": "./src/mcp/index.ts",
|
|
879
|
-
"default": "./src/mcp/index.ts"
|
|
880
|
-
},
|
|
881
|
-
"./orm": {
|
|
882
|
-
"types": "./src/orm/core/index.ts",
|
|
883
|
-
"react-native": "./src/orm/core/index.browser.ts",
|
|
884
|
-
"browser": "./src/orm/core/index.browser.ts",
|
|
885
|
-
"bun": "./src/orm/core/index.bun.ts",
|
|
886
|
-
"import": "./src/orm/core/index.ts",
|
|
887
|
-
"default": "./src/orm/core/index.ts"
|
|
888
|
-
},
|
|
889
|
-
"./orm/postgres": {
|
|
890
|
-
"types": "./src/orm/postgres/index.ts",
|
|
891
|
-
"bun": "./src/orm/postgres/index.bun.ts",
|
|
892
|
-
"import": "./src/orm/postgres/index.ts",
|
|
893
|
-
"default": "./src/orm/postgres/index.ts"
|
|
894
|
-
},
|
|
895
|
-
"./queue": {
|
|
896
|
-
"types": "./src/queue/core/index.ts",
|
|
897
|
-
"workerd": "./src/queue/core/index.workerd.ts",
|
|
898
|
-
"import": "./src/queue/core/index.ts",
|
|
899
|
-
"default": "./src/queue/core/index.ts"
|
|
900
|
-
},
|
|
901
|
-
"./queue/redis": {
|
|
902
|
-
"types": "./src/queue/redis/index.ts",
|
|
903
|
-
"import": "./src/queue/redis/index.ts",
|
|
904
|
-
"default": "./src/queue/redis/index.ts"
|
|
905
|
-
},
|
|
906
|
-
"./react/auth": {
|
|
907
|
-
"types": "./src/react/auth/index.ts",
|
|
908
|
-
"react-native": "./src/react/auth/index.browser.ts",
|
|
909
|
-
"browser": "./src/react/auth/index.browser.ts",
|
|
910
|
-
"import": "./src/react/auth/index.ts",
|
|
911
|
-
"default": "./src/react/auth/index.ts"
|
|
912
|
-
},
|
|
913
|
-
"./react": {
|
|
914
|
-
"types": "./src/react/core/index.ts",
|
|
915
|
-
"import": "./src/react/core/index.ts",
|
|
916
|
-
"default": "./src/react/core/index.ts"
|
|
917
|
-
},
|
|
918
|
-
"./react/form": {
|
|
919
|
-
"types": "./src/react/form/index.ts",
|
|
920
|
-
"import": "./src/react/form/index.ts",
|
|
921
|
-
"default": "./src/react/form/index.ts"
|
|
922
|
-
},
|
|
923
|
-
"./react/head": {
|
|
924
|
-
"types": "./src/react/head/index.ts",
|
|
925
|
-
"react-native": "./src/react/head/index.browser.ts",
|
|
926
|
-
"browser": "./src/react/head/index.browser.ts",
|
|
927
|
-
"import": "./src/react/head/index.ts",
|
|
928
|
-
"default": "./src/react/head/index.ts"
|
|
929
|
-
},
|
|
930
|
-
"./react/i18n": {
|
|
931
|
-
"types": "./src/react/i18n/index.ts",
|
|
932
|
-
"import": "./src/react/i18n/index.ts",
|
|
933
|
-
"default": "./src/react/i18n/index.ts"
|
|
934
|
-
},
|
|
935
|
-
"./react/intro": {
|
|
936
|
-
"types": "./src/react/intro/index.ts",
|
|
937
|
-
"import": "./src/react/intro/index.ts",
|
|
938
|
-
"default": "./src/react/intro/index.ts"
|
|
939
|
-
},
|
|
940
|
-
"./react/router": {
|
|
941
|
-
"types": "./src/react/router/index.ts",
|
|
942
|
-
"react-native": "./src/react/router/index.browser.ts",
|
|
943
|
-
"browser": "./src/react/router/index.browser.ts",
|
|
944
|
-
"import": "./src/react/router/index.ts",
|
|
945
|
-
"default": "./src/react/router/index.ts"
|
|
946
|
-
},
|
|
947
|
-
"./react/testing": {
|
|
948
|
-
"types": "./src/react/testing/index.ts",
|
|
949
|
-
"import": "./src/react/testing/index.ts",
|
|
950
|
-
"default": "./src/react/testing/index.ts"
|
|
951
|
-
},
|
|
952
|
-
"./react/ui": {
|
|
953
|
-
"types": "./src/react/ui/index.ts",
|
|
954
|
-
"import": "./src/react/ui/index.ts",
|
|
955
|
-
"default": "./src/react/ui/index.ts"
|
|
956
|
-
},
|
|
957
|
-
"./react/websocket": {
|
|
958
|
-
"types": "./src/react/websocket/index.ts",
|
|
959
|
-
"import": "./src/react/websocket/index.ts",
|
|
960
|
-
"default": "./src/react/websocket/index.ts"
|
|
961
|
-
},
|
|
962
|
-
"./redis": {
|
|
963
|
-
"types": "./src/redis/index.ts",
|
|
964
|
-
"bun": "./src/redis/index.bun.ts",
|
|
965
|
-
"import": "./src/redis/index.ts",
|
|
966
|
-
"default": "./src/redis/index.ts"
|
|
967
|
-
},
|
|
968
|
-
"./retry": {
|
|
969
|
-
"types": "./src/retry/index.ts",
|
|
970
|
-
"import": "./src/retry/index.ts",
|
|
971
|
-
"default": "./src/retry/index.ts"
|
|
972
|
-
},
|
|
973
|
-
"./router": {
|
|
974
|
-
"types": "./src/router/index.ts",
|
|
975
|
-
"import": "./src/router/index.ts",
|
|
976
|
-
"default": "./src/router/index.ts"
|
|
977
|
-
},
|
|
978
|
-
"./scheduler": {
|
|
979
|
-
"types": "./src/scheduler/index.ts",
|
|
980
|
-
"workerd": "./src/scheduler/index.workerd.ts",
|
|
981
|
-
"import": "./src/scheduler/index.ts",
|
|
982
|
-
"default": "./src/scheduler/index.ts"
|
|
983
|
-
},
|
|
984
|
-
"./security": {
|
|
985
|
-
"types": "./src/security/index.ts",
|
|
986
|
-
"react-native": "./src/security/index.browser.ts",
|
|
987
|
-
"browser": "./src/security/index.browser.ts",
|
|
988
|
-
"import": "./src/security/index.ts",
|
|
989
|
-
"default": "./src/security/index.ts"
|
|
990
|
-
},
|
|
991
|
-
"./server/auth": {
|
|
992
|
-
"types": "./src/server/auth/index.ts",
|
|
993
|
-
"react-native": "./src/server/auth/index.browser.ts",
|
|
994
|
-
"browser": "./src/server/auth/index.browser.ts",
|
|
995
|
-
"import": "./src/server/auth/index.ts",
|
|
996
|
-
"default": "./src/server/auth/index.ts"
|
|
997
|
-
},
|
|
998
|
-
"./server/cookies": {
|
|
999
|
-
"types": "./src/server/cookies/index.ts",
|
|
1000
|
-
"react-native": "./src/server/cookies/index.browser.ts",
|
|
1001
|
-
"browser": "./src/server/cookies/index.browser.ts",
|
|
1002
|
-
"import": "./src/server/cookies/index.ts",
|
|
1003
|
-
"default": "./src/server/cookies/index.ts"
|
|
1004
|
-
},
|
|
1005
|
-
"./server": {
|
|
1006
|
-
"types": "./src/server/core/index.ts",
|
|
1007
|
-
"react-native": "./src/server/core/index.browser.ts",
|
|
1008
|
-
"browser": "./src/server/core/index.browser.ts",
|
|
1009
|
-
"import": "./src/server/core/index.ts",
|
|
1010
|
-
"default": "./src/server/core/index.ts"
|
|
1011
|
-
},
|
|
1012
|
-
"./server/cors": {
|
|
1013
|
-
"types": "./src/server/cors/index.ts",
|
|
1014
|
-
"import": "./src/server/cors/index.ts",
|
|
1015
|
-
"default": "./src/server/cors/index.ts"
|
|
1016
|
-
},
|
|
1017
|
-
"./server/etag": {
|
|
1018
|
-
"types": "./src/server/etag/index.ts",
|
|
1019
|
-
"import": "./src/server/etag/index.ts",
|
|
1020
|
-
"default": "./src/server/etag/index.ts"
|
|
1021
|
-
},
|
|
1022
|
-
"./server/health": {
|
|
1023
|
-
"types": "./src/server/health/index.ts",
|
|
1024
|
-
"import": "./src/server/health/index.ts",
|
|
1025
|
-
"default": "./src/server/health/index.ts"
|
|
1026
|
-
},
|
|
1027
|
-
"./server/links": {
|
|
1028
|
-
"types": "./src/server/links/index.ts",
|
|
1029
|
-
"react-native": "./src/server/links/index.browser.ts",
|
|
1030
|
-
"browser": "./src/server/links/index.browser.ts",
|
|
1031
|
-
"import": "./src/server/links/index.ts",
|
|
1032
|
-
"default": "./src/server/links/index.ts"
|
|
1033
|
-
},
|
|
1034
|
-
"./server/metrics": {
|
|
1035
|
-
"types": "./src/server/metrics/index.ts",
|
|
1036
|
-
"import": "./src/server/metrics/index.ts",
|
|
1037
|
-
"default": "./src/server/metrics/index.ts"
|
|
1038
|
-
},
|
|
1039
|
-
"./server/proxy": {
|
|
1040
|
-
"types": "./src/server/proxy/index.ts",
|
|
1041
|
-
"import": "./src/server/proxy/index.ts",
|
|
1042
|
-
"default": "./src/server/proxy/index.ts"
|
|
1043
|
-
},
|
|
1044
|
-
"./server/rate-limit": {
|
|
1045
|
-
"types": "./src/server/rate-limit/index.ts",
|
|
1046
|
-
"import": "./src/server/rate-limit/index.ts",
|
|
1047
|
-
"default": "./src/server/rate-limit/index.ts"
|
|
1048
|
-
},
|
|
1049
|
-
"./server/static": {
|
|
1050
|
-
"types": "./src/server/static/index.ts",
|
|
1051
|
-
"import": "./src/server/static/index.ts",
|
|
1052
|
-
"default": "./src/server/static/index.ts"
|
|
1053
|
-
},
|
|
1054
|
-
"./server/swagger": {
|
|
1055
|
-
"types": "./src/server/swagger/index.ts",
|
|
1056
|
-
"import": "./src/server/swagger/index.ts",
|
|
1057
|
-
"default": "./src/server/swagger/index.ts"
|
|
1058
|
-
},
|
|
1059
|
-
"./sms": {
|
|
1060
|
-
"types": "./src/sms/index.ts",
|
|
1061
|
-
"import": "./src/sms/index.ts",
|
|
1062
|
-
"default": "./src/sms/index.ts"
|
|
1063
|
-
},
|
|
1064
|
-
"./system": {
|
|
1065
|
-
"types": "./src/system/index.ts",
|
|
1066
|
-
"react-native": "./src/system/index.browser.ts",
|
|
1067
|
-
"workerd": "./src/system/index.workerd.ts",
|
|
1068
|
-
"browser": "./src/system/index.browser.ts",
|
|
1069
|
-
"import": "./src/system/index.ts",
|
|
1070
|
-
"default": "./src/system/index.ts"
|
|
1071
|
-
},
|
|
1072
|
-
"./topic": {
|
|
1073
|
-
"types": "./src/topic/core/index.ts",
|
|
1074
|
-
"import": "./src/topic/core/index.ts",
|
|
1075
|
-
"default": "./src/topic/core/index.ts"
|
|
1076
|
-
},
|
|
1077
|
-
"./topic/redis": {
|
|
1078
|
-
"types": "./src/topic/redis/index.ts",
|
|
1079
|
-
"import": "./src/topic/redis/index.ts",
|
|
1080
|
-
"default": "./src/topic/redis/index.ts"
|
|
1081
|
-
},
|
|
1082
|
-
"./websocket": {
|
|
1083
|
-
"types": "./src/websocket/index.ts",
|
|
1084
|
-
"react-native": "./src/websocket/index.browser.ts",
|
|
1085
|
-
"browser": "./src/websocket/index.browser.ts",
|
|
1086
|
-
"import": "./src/websocket/index.ts",
|
|
1087
|
-
"default": "./src/websocket/index.ts"
|
|
1088
|
-
},
|
|
1089
|
-
"./tsconfig.base": "./tsconfig.base.json",
|
|
1090
|
-
"./package.json": "./package.json"
|
|
1091
|
-
}
|
|
1092
|
-
};
|
|
1093
|
-
const version = version$1;
|
|
761
|
+
const alephaPackageJson = pkg;
|
|
762
|
+
const version = pkg.version;
|
|
1094
763
|
//#endregion
|
|
1095
764
|
//#region ../../src/cli/core/services/PackageManagerUtils.ts
|
|
1096
765
|
/**
|
|
@@ -1285,14 +954,14 @@ var PackageManagerUtils = class {
|
|
|
1285
954
|
"yarn.lock"
|
|
1286
955
|
]);
|
|
1287
956
|
await this.editPackageJson(root, (pkg) => {
|
|
1288
|
-
|
|
957
|
+
pkg.packageManager = void 0;
|
|
1289
958
|
return pkg;
|
|
1290
959
|
});
|
|
1291
960
|
}
|
|
1292
961
|
async removePnpm(root) {
|
|
1293
962
|
await this.removeFiles(root, ["pnpm-lock.yaml", "pnpm-workspace.yaml"]);
|
|
1294
963
|
await this.editPackageJson(root, (pkg) => {
|
|
1295
|
-
|
|
964
|
+
pkg.packageManager = void 0;
|
|
1296
965
|
return pkg;
|
|
1297
966
|
});
|
|
1298
967
|
}
|
|
@@ -1342,7 +1011,7 @@ var PackageManagerUtils = class {
|
|
|
1342
1011
|
const alephaDeps = alephaPackageJson.devDependencies;
|
|
1343
1012
|
const dependencies = { alepha: `^${version}` };
|
|
1344
1013
|
const devDependencies = { vite: alephaDeps.vite };
|
|
1345
|
-
if (!modes.react) devDependencies["drizzle-kit"] = alephaDeps["drizzle-kit"];
|
|
1014
|
+
if (!modes.react || modes.saas) devDependencies["drizzle-kit"] = alephaDeps["drizzle-kit"];
|
|
1346
1015
|
if (!modes.isPackage) {
|
|
1347
1016
|
devDependencies["@biomejs/biome"] = alephaDeps["@biomejs/biome"];
|
|
1348
1017
|
if (modes.test) devDependencies.vitest = alephaDeps.vitest;
|
|
@@ -1356,9 +1025,10 @@ var PackageManagerUtils = class {
|
|
|
1356
1025
|
};
|
|
1357
1026
|
if (modes.test) scripts.test = "vitest run";
|
|
1358
1027
|
if (modes.tailwind) {
|
|
1359
|
-
devDependencies.tailwindcss =
|
|
1360
|
-
devDependencies["@tailwindcss/vite"] = "
|
|
1028
|
+
devDependencies.tailwindcss = alephaDeps.tailwindcss;
|
|
1029
|
+
devDependencies["@tailwindcss/vite"] = alephaDeps["@tailwindcss/vite"];
|
|
1361
1030
|
}
|
|
1031
|
+
if (modes.shadcn) devDependencies.shadcn = alephaDeps.shadcn;
|
|
1362
1032
|
if (modes.react) {
|
|
1363
1033
|
dependencies.react = alephaDeps.react;
|
|
1364
1034
|
dependencies["react-dom"] = alephaDeps["react-dom"];
|
|
@@ -1380,8 +1050,8 @@ var PackageManagerUtils = class {
|
|
|
1380
1050
|
};
|
|
1381
1051
|
//#endregion
|
|
1382
1052
|
//#region ../../src/cli/core/templates/agentMd.ts
|
|
1383
|
-
const agentMd = (
|
|
1384
|
-
return
|
|
1053
|
+
const agentMd = () => {
|
|
1054
|
+
return `# AGENTS.md
|
|
1385
1055
|
|
|
1386
1056
|
This is an **Alepha** project.
|
|
1387
1057
|
|
|
@@ -1472,7 +1142,19 @@ export type HelloResponse = Static<typeof helloResponseSchema>;
|
|
|
1472
1142
|
//#endregion
|
|
1473
1143
|
//#region ../../src/cli/core/templates/apiIndexTs.ts
|
|
1474
1144
|
const apiIndexTs = (options = {}) => {
|
|
1475
|
-
const { appName = "app" } = options;
|
|
1145
|
+
const { appName = "app", saas = false } = options;
|
|
1146
|
+
if (saas) return `
|
|
1147
|
+
import { $module } from "alepha";
|
|
1148
|
+
import { AlephaApiUsers } from "alepha/api/users";
|
|
1149
|
+
import { HelloController } from "./controllers/HelloController.ts";
|
|
1150
|
+
import { RealmProvider } from "./providers/RealmProvider.ts";
|
|
1151
|
+
|
|
1152
|
+
export const ApiModule = $module({
|
|
1153
|
+
name: "${appName}.api",
|
|
1154
|
+
services: [HelloController, RealmProvider],
|
|
1155
|
+
imports: [AlephaApiUsers],
|
|
1156
|
+
});
|
|
1157
|
+
`.trim();
|
|
1476
1158
|
return `
|
|
1477
1159
|
import { $module } from "alepha";
|
|
1478
1160
|
import { HelloController } from "./controllers/HelloController.ts";
|
|
@@ -1519,6 +1201,46 @@ const biomeJson = () => `
|
|
|
1519
1201
|
}
|
|
1520
1202
|
`.trim();
|
|
1521
1203
|
//#endregion
|
|
1204
|
+
//#region ../../src/cli/core/templates/componentsJsonTs.ts
|
|
1205
|
+
/**
|
|
1206
|
+
* `components.json` is the shadcn CLI's project config — it tells
|
|
1207
|
+
* `shadcn add` where to drop primitives, which tailwind tokens to use,
|
|
1208
|
+
* which icon library to wire up, and which custom registries to resolve.
|
|
1209
|
+
*
|
|
1210
|
+
* Aliases follow shadcn's defaults (`@/components`, `@/lib/utils`) so the
|
|
1211
|
+
* CLI honors them across `init` + `add` calls. Alepha app code lives at
|
|
1212
|
+
* `src/web/` (Home, AppRouter, …) and the shadcn primitives live at
|
|
1213
|
+
* `src/components/` — kept separate to make the registry components
|
|
1214
|
+
* trivially upgradable via `shadcn add --overwrite`.
|
|
1215
|
+
*
|
|
1216
|
+
* The `registries` block pre-wires the public Alepha registry — consumers
|
|
1217
|
+
* can immediately run e.g. `shadcn add @alepha/auth-login`.
|
|
1218
|
+
*/
|
|
1219
|
+
const componentsJsonTs = () => `{
|
|
1220
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
1221
|
+
"style": "new-york",
|
|
1222
|
+
"rsc": false,
|
|
1223
|
+
"tsx": true,
|
|
1224
|
+
"tailwind": {
|
|
1225
|
+
"config": "",
|
|
1226
|
+
"css": "src/main.css",
|
|
1227
|
+
"baseColor": "neutral",
|
|
1228
|
+
"cssVariables": true
|
|
1229
|
+
},
|
|
1230
|
+
"aliases": {
|
|
1231
|
+
"components": "@/components",
|
|
1232
|
+
"utils": "@/lib/utils",
|
|
1233
|
+
"ui": "@/components/ui",
|
|
1234
|
+
"lib": "@/lib",
|
|
1235
|
+
"hooks": "@/hooks"
|
|
1236
|
+
},
|
|
1237
|
+
"iconLibrary": "lucide",
|
|
1238
|
+
"registries": {
|
|
1239
|
+
"@alepha": "https://alepha.dev/r/{name}.json"
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
`;
|
|
1243
|
+
//#endregion
|
|
1522
1244
|
//#region ../../src/cli/core/templates/dummySpecTs.ts
|
|
1523
1245
|
const dummySpecTs = () => `
|
|
1524
1246
|
import { test, expect } from "vitest";
|
|
@@ -1660,6 +1382,7 @@ const mainCss = (opts = {}) => {
|
|
|
1660
1382
|
*
|
|
1661
1383
|
* Options:
|
|
1662
1384
|
* - Tailwind CSS: Use \`alepha init --tailwind\` to add Tailwind CSS
|
|
1385
|
+
* - shadcn/ui: Use \`alepha init --shadcn\` to add shadcn/ui setup
|
|
1663
1386
|
* - Raw CSS: Write your own styles below
|
|
1664
1387
|
*/
|
|
1665
1388
|
|
|
@@ -1690,6 +1413,225 @@ run(alepha);
|
|
|
1690
1413
|
`.trim();
|
|
1691
1414
|
};
|
|
1692
1415
|
//#endregion
|
|
1416
|
+
//#region ../../src/cli/core/templates/saasAdminLayoutTsx.ts
|
|
1417
|
+
/**
|
|
1418
|
+
* SaaS admin layout — full AppShell on /admin with a sidebar, breadcrumbs,
|
|
1419
|
+
* a Sonner toaster, and a confirm provider. The page list grows with
|
|
1420
|
+
* whatever `admin-*` registry components the user adds.
|
|
1421
|
+
*
|
|
1422
|
+
* All UI primitives come from `src/components/*` where `shadcn add` drops
|
|
1423
|
+
* them; alepha app code lives in `src/web/*` and references them via the
|
|
1424
|
+
* `@/components/*` alias.
|
|
1425
|
+
*/
|
|
1426
|
+
const saasAdminLayoutTsx = () => `import { AppShell } from "@/components/app-shell";
|
|
1427
|
+
import { Toaster } from "@/components/ui/sonner";
|
|
1428
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
1429
|
+
import { DialogProvider } from "@/components/use-dialog";
|
|
1430
|
+
import { NestedView, useRouterState } from "alepha/react/router";
|
|
1431
|
+
import { ShieldCheck, Users } from "lucide-react";
|
|
1432
|
+
|
|
1433
|
+
const NAV = [
|
|
1434
|
+
{
|
|
1435
|
+
label: "Identity",
|
|
1436
|
+
items: [
|
|
1437
|
+
{ href: "/admin/users", label: "Users", icon: Users },
|
|
1438
|
+
{ href: "/admin/sessions", label: "Sessions", icon: ShieldCheck },
|
|
1439
|
+
],
|
|
1440
|
+
},
|
|
1441
|
+
] as const;
|
|
1442
|
+
|
|
1443
|
+
const findCrumbs = (pathname: string): { label: string; href?: string }[] => {
|
|
1444
|
+
for (const group of NAV) {
|
|
1445
|
+
const match = group.items.find((it) => it.href === pathname);
|
|
1446
|
+
if (match) return [{ label: group.label }, { label: match.label }];
|
|
1447
|
+
}
|
|
1448
|
+
return [];
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
const AdminLayout = () => {
|
|
1452
|
+
const state = useRouterState();
|
|
1453
|
+
const crumbs = findCrumbs(state.url.pathname);
|
|
1454
|
+
|
|
1455
|
+
return (
|
|
1456
|
+
<TooltipProvider>
|
|
1457
|
+
<DialogProvider>
|
|
1458
|
+
<AppShell
|
|
1459
|
+
brand={
|
|
1460
|
+
<a
|
|
1461
|
+
href="/admin"
|
|
1462
|
+
className="flex items-center gap-2 px-2 py-2 font-semibold group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0"
|
|
1463
|
+
>
|
|
1464
|
+
<span className="bg-primary text-primary-foreground flex size-7 shrink-0 items-center justify-center rounded">
|
|
1465
|
+
α
|
|
1466
|
+
</span>
|
|
1467
|
+
<span className="truncate group-data-[collapsible=icon]:hidden">
|
|
1468
|
+
Admin
|
|
1469
|
+
</span>
|
|
1470
|
+
</a>
|
|
1471
|
+
}
|
|
1472
|
+
nav={NAV.map((group) => ({
|
|
1473
|
+
label: group.label,
|
|
1474
|
+
items: group.items.map((it) => ({
|
|
1475
|
+
href: it.href,
|
|
1476
|
+
label: it.label,
|
|
1477
|
+
icon: it.icon,
|
|
1478
|
+
active: it.href === state.url.pathname,
|
|
1479
|
+
})),
|
|
1480
|
+
}))}
|
|
1481
|
+
breadcrumbs={crumbs.length ? crumbs : undefined}
|
|
1482
|
+
>
|
|
1483
|
+
<NestedView />
|
|
1484
|
+
</AppShell>
|
|
1485
|
+
<Toaster />
|
|
1486
|
+
</DialogProvider>
|
|
1487
|
+
</TooltipProvider>
|
|
1488
|
+
);
|
|
1489
|
+
};
|
|
1490
|
+
|
|
1491
|
+
export default AdminLayout;
|
|
1492
|
+
`;
|
|
1493
|
+
//#endregion
|
|
1494
|
+
//#region ../../src/cli/core/templates/saasAdminPagesTsx.ts
|
|
1495
|
+
/**
|
|
1496
|
+
* Admin pages — each is a thin wrapper around the matching `admin-*`
|
|
1497
|
+
* registry component (placed at `src/components/admin/*`). The starter
|
|
1498
|
+
* ships with Users + Sessions; add more by `shadcn add @alepha/admin-…`
|
|
1499
|
+
* and a matching `$page(...)` in AppRouter.
|
|
1500
|
+
*/
|
|
1501
|
+
const saasAdminUsersTsx = () => `import { AdminUsers } from "@/components/admin/admin-users";
|
|
1502
|
+
|
|
1503
|
+
const AdminUsersPage = () => {
|
|
1504
|
+
return <AdminUsers />;
|
|
1505
|
+
};
|
|
1506
|
+
|
|
1507
|
+
export default AdminUsersPage;
|
|
1508
|
+
`;
|
|
1509
|
+
const saasAdminSessionsTsx = () => `import { AdminSessions } from "@/components/admin/admin-sessions";
|
|
1510
|
+
|
|
1511
|
+
const AdminSessionsPage = () => {
|
|
1512
|
+
return <AdminSessions />;
|
|
1513
|
+
};
|
|
1514
|
+
|
|
1515
|
+
export default AdminSessionsPage;
|
|
1516
|
+
`;
|
|
1517
|
+
//#endregion
|
|
1518
|
+
//#region ../../src/cli/core/templates/saasAuthLayoutTsx.ts
|
|
1519
|
+
/**
|
|
1520
|
+
* SaaS auth layout — wraps every /auth/* page with a centered card.
|
|
1521
|
+
* Routes (login, register, reset-password, verify-email) are mounted as
|
|
1522
|
+
* children so they share this shell.
|
|
1523
|
+
*/
|
|
1524
|
+
const saasAuthLayoutTsx = () => `import { NestedView } from "alepha/react/router";
|
|
1525
|
+
|
|
1526
|
+
const AuthLayout = () => {
|
|
1527
|
+
return (
|
|
1528
|
+
<div className="bg-background flex min-h-screen items-center justify-center p-4">
|
|
1529
|
+
<div className="w-full max-w-md">
|
|
1530
|
+
<NestedView />
|
|
1531
|
+
</div>
|
|
1532
|
+
</div>
|
|
1533
|
+
);
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1536
|
+
export default AuthLayout;
|
|
1537
|
+
`;
|
|
1538
|
+
//#endregion
|
|
1539
|
+
//#region ../../src/cli/core/templates/saasAuthPagesTsx.ts
|
|
1540
|
+
/**
|
|
1541
|
+
* Per-page wrapper around the registry components. The registry component
|
|
1542
|
+
* receives the realm config from the page loader; the page itself stays a
|
|
1543
|
+
* thin shell so apps can layer their branding around it.
|
|
1544
|
+
*
|
|
1545
|
+
* Registry components land at `src/components/auth/*` (shadcn defaults).
|
|
1546
|
+
*/
|
|
1547
|
+
const saasAuthLoginTsx = () => `import { AuthLogin } from "@/components/auth/auth-login";
|
|
1548
|
+
import type { RealmConfig } from "alepha/api/users";
|
|
1549
|
+
|
|
1550
|
+
export interface AuthLoginPageProps {
|
|
1551
|
+
realmConfig: RealmConfig;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
const AuthLoginPage = (props: AuthLoginPageProps) => {
|
|
1555
|
+
return <AuthLogin realmConfig={props.realmConfig} />;
|
|
1556
|
+
};
|
|
1557
|
+
|
|
1558
|
+
export default AuthLoginPage;
|
|
1559
|
+
`;
|
|
1560
|
+
const saasAuthRegisterTsx = () => `import { AuthRegister } from "@/components/auth/auth-register";
|
|
1561
|
+
import type { RealmConfig } from "alepha/api/users";
|
|
1562
|
+
|
|
1563
|
+
export interface AuthRegisterPageProps {
|
|
1564
|
+
realmConfig: RealmConfig;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
const AuthRegisterPage = (props: AuthRegisterPageProps) => {
|
|
1568
|
+
return <AuthRegister realmConfig={props.realmConfig} />;
|
|
1569
|
+
};
|
|
1570
|
+
|
|
1571
|
+
export default AuthRegisterPage;
|
|
1572
|
+
`;
|
|
1573
|
+
const saasAuthResetPasswordTsx = () => `import { AuthResetPassword } from "@/components/auth/auth-reset-password";
|
|
1574
|
+
import type { RealmConfig } from "alepha/api/users";
|
|
1575
|
+
|
|
1576
|
+
export interface AuthResetPasswordPageProps {
|
|
1577
|
+
realmConfig: RealmConfig;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
const AuthResetPasswordPage = (props: AuthResetPasswordPageProps) => {
|
|
1581
|
+
return <AuthResetPassword realmConfig={props.realmConfig} />;
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
export default AuthResetPasswordPage;
|
|
1585
|
+
`;
|
|
1586
|
+
const saasAuthVerifyEmailTsx = () => `import { AuthVerifyEmail } from "@/components/auth/auth-verify-email";
|
|
1587
|
+
|
|
1588
|
+
const AuthVerifyEmailPage = () => {
|
|
1589
|
+
return <AuthVerifyEmail />;
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
export default AuthVerifyEmailPage;
|
|
1593
|
+
`;
|
|
1594
|
+
//#endregion
|
|
1595
|
+
//#region ../../src/cli/core/templates/saasRealmProviderTs.ts
|
|
1596
|
+
/**
|
|
1597
|
+
* Realm provider scaffolded by `alepha init --saas`.
|
|
1598
|
+
*
|
|
1599
|
+
* Minimal hello-world setup: credentials login with email, one admin seeded
|
|
1600
|
+
* with the developer's git email at scaffold time, and an `admin:ui`
|
|
1601
|
+
* permission used by the AppRouter to gate `/admin/*`. The default `admin`
|
|
1602
|
+
* role grants `*` (so it inherits `admin:ui`); the default `user` role
|
|
1603
|
+
* excludes `admin:*` (so non-admins get a 403 before the shell renders).
|
|
1604
|
+
*
|
|
1605
|
+
* Add `$env`, more permissions, or stricter settings as the project grows.
|
|
1606
|
+
*/
|
|
1607
|
+
const saasRealmProviderTs = (options = {}) => {
|
|
1608
|
+
const adminEmail = options.adminEmail ?? "admin@example.com";
|
|
1609
|
+
return `import { $realm } from "alepha/api/users";
|
|
1610
|
+
import { $permission } from "alepha/security";
|
|
1611
|
+
|
|
1612
|
+
export class RealmProvider {
|
|
1613
|
+
/**
|
|
1614
|
+
* Permission required to open the admin UI. Wired into AppRouter.adminLayout
|
|
1615
|
+
* via \`$secure({ permissions: ["admin:ui"] })\`.
|
|
1616
|
+
*/
|
|
1617
|
+
adminUi = $permission({
|
|
1618
|
+
group: "admin",
|
|
1619
|
+
name: "ui",
|
|
1620
|
+
description: "Access to the admin UI shell",
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1623
|
+
realm = $realm({
|
|
1624
|
+
settings: {
|
|
1625
|
+
adminEmails: [${JSON.stringify(adminEmail)}],
|
|
1626
|
+
},
|
|
1627
|
+
identities: {
|
|
1628
|
+
credentials: true,
|
|
1629
|
+
},
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
`;
|
|
1633
|
+
};
|
|
1634
|
+
//#endregion
|
|
1693
1635
|
//#region ../../src/cli/core/templates/tsconfigJson.ts
|
|
1694
1636
|
const tsconfigJson = () => `
|
|
1695
1637
|
{
|
|
@@ -1733,6 +1675,10 @@ export default defineConfig({
|
|
|
1733
1675
|
//#endregion
|
|
1734
1676
|
//#region ../../src/cli/core/templates/webAppRouterTs.ts
|
|
1735
1677
|
const webAppRouterTs = (options) => {
|
|
1678
|
+
if (options.saas) return saasAppRouterTs();
|
|
1679
|
+
return basicAppRouterTs(options);
|
|
1680
|
+
};
|
|
1681
|
+
const basicAppRouterTs = (options) => {
|
|
1736
1682
|
const imports = ["import { $page } from \"alepha/react/router\";"];
|
|
1737
1683
|
const classMembers = [];
|
|
1738
1684
|
if (options.api) {
|
|
@@ -1754,6 +1700,95 @@ export class AppRouter {
|
|
|
1754
1700
|
${classMembers.join("\n\n")}
|
|
1755
1701
|
}`;
|
|
1756
1702
|
};
|
|
1703
|
+
/**
|
|
1704
|
+
* SaaS router wires three trees onto the app:
|
|
1705
|
+
* / → Home
|
|
1706
|
+
* /auth/* → AuthLayout + login / register / reset / verify
|
|
1707
|
+
* /admin/* → AdminLayout + users / sessions / api-keys / parameters / audits
|
|
1708
|
+
*
|
|
1709
|
+
* Each auth page resolves the realm config from its loader, so the registry
|
|
1710
|
+
* components render with everything they need on first paint.
|
|
1711
|
+
*/
|
|
1712
|
+
const saasAppRouterTs = () => `import type { RealmController } from "alepha/api/users";
|
|
1713
|
+
import { $page, NotFound } from "alepha/react/router";
|
|
1714
|
+
import { $secure } from "alepha/security";
|
|
1715
|
+
import { $client } from "alepha/server/links";
|
|
1716
|
+
import type { HelloController } from "../api/controllers/HelloController.ts";
|
|
1717
|
+
|
|
1718
|
+
export class AppRouter {
|
|
1719
|
+
protected readonly api = $client<HelloController>();
|
|
1720
|
+
protected readonly realmApi = $client<RealmController>();
|
|
1721
|
+
|
|
1722
|
+
home = $page({
|
|
1723
|
+
path: "/",
|
|
1724
|
+
lazy: () => import("./components/Home.tsx"),
|
|
1725
|
+
loader: () => this.api.hello(),
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
// ── /auth — login, register, reset, verify ─────────────────────────────
|
|
1729
|
+
authLayout = $page({
|
|
1730
|
+
path: "/auth",
|
|
1731
|
+
lazy: () => import("./components/auth/AuthLayout.tsx"),
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
login = $page({
|
|
1735
|
+
parent: this.authLayout,
|
|
1736
|
+
path: "/login",
|
|
1737
|
+
name: "login",
|
|
1738
|
+
head: { title: "Sign in" },
|
|
1739
|
+
lazy: () => import("./components/auth/Login.tsx"),
|
|
1740
|
+
loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
register = $page({
|
|
1744
|
+
parent: this.authLayout,
|
|
1745
|
+
path: "/register",
|
|
1746
|
+
name: "register",
|
|
1747
|
+
head: { title: "Sign up" },
|
|
1748
|
+
lazy: () => import("./components/auth/Register.tsx"),
|
|
1749
|
+
loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
|
|
1750
|
+
});
|
|
1751
|
+
|
|
1752
|
+
resetPassword = $page({
|
|
1753
|
+
parent: this.authLayout,
|
|
1754
|
+
path: "/reset-password",
|
|
1755
|
+
head: { title: "Reset password" },
|
|
1756
|
+
lazy: () => import("./components/auth/ResetPassword.tsx"),
|
|
1757
|
+
loader: async () => ({ realmConfig: await this.realmApi.getRealmConfig() }),
|
|
1758
|
+
});
|
|
1759
|
+
|
|
1760
|
+
verifyEmail = $page({
|
|
1761
|
+
parent: this.authLayout,
|
|
1762
|
+
path: "/verify-email",
|
|
1763
|
+
head: { title: "Verify email" },
|
|
1764
|
+
lazy: () => import("./components/auth/VerifyEmail.tsx"),
|
|
1765
|
+
});
|
|
1766
|
+
|
|
1767
|
+
// ── /admin — gated by 'admin:ui' permission, declared in RealmProvider.
|
|
1768
|
+
// Children inherit the gate via the parent chain.
|
|
1769
|
+
adminLayout = $page({
|
|
1770
|
+
path: "/admin",
|
|
1771
|
+
use: [$secure({ permissions: ["admin:ui"] })],
|
|
1772
|
+
lazy: () => import("./components/admin/AdminLayout.tsx"),
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
adminUsers = $page({
|
|
1776
|
+
parent: this.adminLayout,
|
|
1777
|
+
path: "/users",
|
|
1778
|
+
head: { title: "Users" },
|
|
1779
|
+
lazy: () => import("./components/admin/Users.tsx"),
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
adminSessions = $page({
|
|
1783
|
+
parent: this.adminLayout,
|
|
1784
|
+
path: "/sessions",
|
|
1785
|
+
head: { title: "Sessions" },
|
|
1786
|
+
lazy: () => import("./components/admin/Sessions.tsx"),
|
|
1787
|
+
});
|
|
1788
|
+
|
|
1789
|
+
notFound = $page({ path: "/*", component: NotFound });
|
|
1790
|
+
}
|
|
1791
|
+
`;
|
|
1757
1792
|
//#endregion
|
|
1758
1793
|
//#region ../../src/cli/core/templates/webHomeComponentTsx.ts
|
|
1759
1794
|
const webHomeComponentTsx = (options = {}) => {
|
|
@@ -1782,7 +1817,19 @@ export default Home;
|
|
|
1782
1817
|
//#endregion
|
|
1783
1818
|
//#region ../../src/cli/core/templates/webIndexTs.ts
|
|
1784
1819
|
const webIndexTs = (options = {}) => {
|
|
1785
|
-
const { appName = "app" } = options;
|
|
1820
|
+
const { appName = "app", saas = false } = options;
|
|
1821
|
+
if (saas) return `
|
|
1822
|
+
import { $module } from "alepha";
|
|
1823
|
+
import { AlephaReactAuth } from "alepha/react/auth";
|
|
1824
|
+
import { AlephaReactI18n } from "alepha/react/i18n";
|
|
1825
|
+
import { AppRouter } from "./AppRouter.ts";
|
|
1826
|
+
|
|
1827
|
+
export const WebModule = $module({
|
|
1828
|
+
name: "${appName}.web",
|
|
1829
|
+
services: [AppRouter],
|
|
1830
|
+
imports: [AlephaReactAuth, AlephaReactI18n],
|
|
1831
|
+
});
|
|
1832
|
+
`.trim();
|
|
1786
1833
|
return `
|
|
1787
1834
|
import { $module } from "alepha";
|
|
1788
1835
|
import { AppRouter } from "./AppRouter.ts";
|
|
@@ -1808,6 +1855,7 @@ var ProjectScaffolder = class {
|
|
|
1808
1855
|
log = $logger();
|
|
1809
1856
|
colors = $inject(ConsoleColorProvider);
|
|
1810
1857
|
fs = $inject(FileSystemProvider);
|
|
1858
|
+
shell = $inject(ShellProvider);
|
|
1811
1859
|
pm = $inject(PackageManagerUtils);
|
|
1812
1860
|
utils = $inject(AlephaCliUtils);
|
|
1813
1861
|
/**
|
|
@@ -1829,7 +1877,10 @@ var ProjectScaffolder = class {
|
|
|
1829
1877
|
const force = opts.force ?? false;
|
|
1830
1878
|
const checkWorkspace = opts.checkWorkspace ?? false;
|
|
1831
1879
|
if (opts.packageJson) tasks.push(this.pm.ensurePackageJson(root, typeof opts.packageJson === "boolean" ? {} : opts.packageJson).then(() => {}));
|
|
1832
|
-
if (opts.tsconfigJson) tasks.push(this.ensureTsConfig(root, {
|
|
1880
|
+
if (opts.tsconfigJson) tasks.push(this.ensureTsConfig(root, {
|
|
1881
|
+
force,
|
|
1882
|
+
localOnly: opts.tsconfigJson === "local"
|
|
1883
|
+
}));
|
|
1833
1884
|
if (opts.biomeJson) tasks.push(this.ensureBiomeConfig(root, {
|
|
1834
1885
|
force,
|
|
1835
1886
|
checkWorkspace
|
|
@@ -1838,14 +1889,12 @@ var ProjectScaffolder = class {
|
|
|
1838
1889
|
force,
|
|
1839
1890
|
checkWorkspace
|
|
1840
1891
|
}));
|
|
1841
|
-
if (opts.agentMd) tasks.push(this.ensureAgentMd(root, {
|
|
1842
|
-
...opts.agentMd,
|
|
1843
|
-
force
|
|
1844
|
-
}));
|
|
1892
|
+
if (opts.agentMd) tasks.push(this.ensureAgentMd(root, { force }));
|
|
1845
1893
|
await Promise.all(tasks);
|
|
1846
1894
|
}
|
|
1847
1895
|
async ensureTsConfig(root, opts = {}) {
|
|
1848
|
-
|
|
1896
|
+
const exists = opts.localOnly ? await this.fs.exists(this.fs.join(root, "tsconfig.json")) : await this.existsInParents(root, "tsconfig.json");
|
|
1897
|
+
if (!opts.force && exists) return;
|
|
1849
1898
|
await this.fs.writeFile(this.fs.join(root, "tsconfig.json"), tsconfigJson());
|
|
1850
1899
|
}
|
|
1851
1900
|
async ensureBiomeConfig(root, opts = {}) {
|
|
@@ -1872,9 +1921,13 @@ var ProjectScaffolder = class {
|
|
|
1872
1921
|
await this.ensureFile(root, ".gitignore", gitignore(), opts.force);
|
|
1873
1922
|
return true;
|
|
1874
1923
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1924
|
+
/**
|
|
1925
|
+
* Ensure AGENTS.md (cross-tool standard, canonical source) exists, with a
|
|
1926
|
+
* CLAUDE.md stub that imports it via Claude Code's `@` syntax. Single
|
|
1927
|
+
* source of truth, cross-platform, no symlink needed.
|
|
1928
|
+
*/
|
|
1929
|
+
async ensureAgentMd(root, options = {}) {
|
|
1930
|
+
await Promise.all([this.ensureFile(root, "AGENTS.md", agentMd(), options.force), this.ensureFile(root, "CLAUDE.md", "@AGENTS.md\n", options.force)]);
|
|
1878
1931
|
}
|
|
1879
1932
|
/**
|
|
1880
1933
|
* Ensure alepha.config.ts exists with documented options.
|
|
@@ -1904,9 +1957,32 @@ var ProjectScaffolder = class {
|
|
|
1904
1957
|
const appName = this.getAppName(root);
|
|
1905
1958
|
await this.fs.mkdir(this.fs.join(root, "src/api/controllers"), { recursive: true });
|
|
1906
1959
|
await this.fs.mkdir(this.fs.join(root, "src/api/schemas"), { recursive: true });
|
|
1907
|
-
await this.ensureFile(root, "src/api/index.ts", apiIndexTs({
|
|
1960
|
+
await this.ensureFile(root, "src/api/index.ts", apiIndexTs({
|
|
1961
|
+
appName,
|
|
1962
|
+
saas: opts.saas
|
|
1963
|
+
}), opts.force);
|
|
1908
1964
|
await this.ensureFile(root, "src/api/controllers/HelloController.ts", apiHelloControllerTs({ appName }), opts.force);
|
|
1909
1965
|
await this.ensureFile(root, "src/api/schemas/helloResponseSchema.ts", apiHelloResponseSchemaTs(), opts.force);
|
|
1966
|
+
if (opts.saas) {
|
|
1967
|
+
await this.fs.mkdir(this.fs.join(root, "src/api/providers"), { recursive: true });
|
|
1968
|
+
const adminEmail = await this.detectGitEmail();
|
|
1969
|
+
await this.ensureFile(root, "src/api/providers/RealmProvider.ts", saasRealmProviderTs({ adminEmail }), opts.force);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Best-effort lookup for the developer's git email (used as the seeded
|
|
1974
|
+
* `adminEmails` entry in the SaaS realm). Returns undefined if git isn't
|
|
1975
|
+
* available or if `user.email` isn't configured — the template falls back
|
|
1976
|
+
* to `admin@example.com` in that case.
|
|
1977
|
+
*/
|
|
1978
|
+
async detectGitEmail() {
|
|
1979
|
+
try {
|
|
1980
|
+
const email = (await this.shell.run("git config --get user.email", { capture: true }) ?? "").trim();
|
|
1981
|
+
if (!email || !email.includes("@")) return void 0;
|
|
1982
|
+
return email;
|
|
1983
|
+
} catch {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1910
1986
|
}
|
|
1911
1987
|
/**
|
|
1912
1988
|
* Ensure web/React project structure exists.
|
|
@@ -1919,14 +1995,35 @@ var ProjectScaffolder = class {
|
|
|
1919
1995
|
async ensureWebProject(root, opts = {}) {
|
|
1920
1996
|
const appName = this.getAppName(root);
|
|
1921
1997
|
await this.fs.mkdir(this.fs.join(root, "src/web/components"), { recursive: true });
|
|
1998
|
+
if (opts.saas) {
|
|
1999
|
+
await this.fs.mkdir(this.fs.join(root, "src/web/components/auth"), { recursive: true });
|
|
2000
|
+
await this.fs.mkdir(this.fs.join(root, "src/web/components/admin"), { recursive: true });
|
|
2001
|
+
}
|
|
1922
2002
|
await this.fs.mkdir(this.fs.join(root, "public"), { recursive: true });
|
|
1923
2003
|
await this.ensureFile(root, "public/favicon.svg", logoSvg, opts.force);
|
|
1924
2004
|
await this.ensureFile(root, "src/main.css", mainCss({ tailwind: opts.tailwind }), opts.force);
|
|
1925
2005
|
if (opts.tailwind) await this.ensureFile(root, "vite.config.ts", viteConfigTs(), opts.force);
|
|
1926
|
-
await this.ensureFile(root, "
|
|
1927
|
-
await this.ensureFile(root, "src/web/
|
|
2006
|
+
if (opts.shadcn) await this.ensureFile(root, "components.json", componentsJsonTs(), opts.force);
|
|
2007
|
+
await this.ensureFile(root, "src/web/index.ts", webIndexTs({
|
|
2008
|
+
appName,
|
|
2009
|
+
saas: opts.saas
|
|
2010
|
+
}), opts.force);
|
|
2011
|
+
await this.ensureFile(root, "src/web/AppRouter.ts", webAppRouterTs({
|
|
2012
|
+
api: opts.api,
|
|
2013
|
+
saas: opts.saas
|
|
2014
|
+
}), opts.force);
|
|
1928
2015
|
await this.ensureFile(root, "src/web/components/Home.tsx", webHomeComponentTsx({ api: opts.api }), opts.force);
|
|
1929
2016
|
await this.ensureFile(root, "src/main.browser.ts", mainBrowserTs(), opts.force);
|
|
2017
|
+
if (opts.saas) {
|
|
2018
|
+
await this.ensureFile(root, "src/web/components/auth/AuthLayout.tsx", saasAuthLayoutTsx(), opts.force);
|
|
2019
|
+
await this.ensureFile(root, "src/web/components/auth/Login.tsx", saasAuthLoginTsx(), opts.force);
|
|
2020
|
+
await this.ensureFile(root, "src/web/components/auth/Register.tsx", saasAuthRegisterTsx(), opts.force);
|
|
2021
|
+
await this.ensureFile(root, "src/web/components/auth/ResetPassword.tsx", saasAuthResetPasswordTsx(), opts.force);
|
|
2022
|
+
await this.ensureFile(root, "src/web/components/auth/VerifyEmail.tsx", saasAuthVerifyEmailTsx(), opts.force);
|
|
2023
|
+
await this.ensureFile(root, "src/web/components/admin/AdminLayout.tsx", saasAdminLayoutTsx(), opts.force);
|
|
2024
|
+
await this.ensureFile(root, "src/web/components/admin/Users.tsx", saasAdminUsersTsx(), opts.force);
|
|
2025
|
+
await this.ensureFile(root, "src/web/components/admin/Sessions.tsx", saasAdminSessionsTsx(), opts.force);
|
|
2026
|
+
}
|
|
1930
2027
|
}
|
|
1931
2028
|
/**
|
|
1932
2029
|
* Ensure test directory exists with a dummy test file + a self-contained
|
|
@@ -1953,13 +2050,21 @@ var ProjectScaffolder = class {
|
|
|
1953
2050
|
root = this.fs.join(root, args);
|
|
1954
2051
|
await this.fs.mkdir(root, { force: true });
|
|
1955
2052
|
}
|
|
2053
|
+
const shadcnPreset = typeof flags.saas === "string" && flags.saas || typeof flags.shadcn === "string" && flags.shadcn || "b0";
|
|
2054
|
+
const f = flags;
|
|
2055
|
+
f.shadcn = !!flags.shadcn;
|
|
2056
|
+
f.saas = !!flags.saas;
|
|
2057
|
+
if (f.saas) {
|
|
2058
|
+
f.shadcn = true;
|
|
2059
|
+
f.api = true;
|
|
2060
|
+
}
|
|
2061
|
+
if (f.shadcn) f.tailwind = true;
|
|
1956
2062
|
if (flags.tailwind) flags.react = true;
|
|
1957
|
-
if ((flags.api || flags.react || flags.tailwind) && !flags.force) {
|
|
2063
|
+
if ((flags.api || flags.react || flags.tailwind || flags.shadcn || flags.saas) && !flags.force) {
|
|
1958
2064
|
if ((await this.fs.ls(root)).filter((f) => f !== "package.json").length > 0) throw new AlephaError(`Target directory is not empty (${root}). Use --force to overwrite existing files.`);
|
|
1959
2065
|
}
|
|
1960
2066
|
const workspace = await this.pm.getWorkspaceContext(root);
|
|
1961
|
-
|
|
1962
|
-
if (!workspace.isPackage) agentType = await this.utils.isInstalledAsync("claude") ? "claude" : "agents";
|
|
2067
|
+
const writeAgentMd = !workspace.isPackage;
|
|
1963
2068
|
const isExpo = await this.pm.hasExpo(root);
|
|
1964
2069
|
const force = !!flags.force;
|
|
1965
2070
|
await run({
|
|
@@ -1968,13 +2073,13 @@ var ProjectScaffolder = class {
|
|
|
1968
2073
|
await this.ensureConfig(root, {
|
|
1969
2074
|
force,
|
|
1970
2075
|
packageJson: {
|
|
1971
|
-
...
|
|
2076
|
+
...f,
|
|
1972
2077
|
isPackage: workspace.isPackage
|
|
1973
2078
|
},
|
|
1974
|
-
tsconfigJson: !workspace.config.tsconfigJson,
|
|
2079
|
+
tsconfigJson: f.shadcn ? "local" : !workspace.config.tsconfigJson,
|
|
1975
2080
|
biomeJson: true,
|
|
1976
2081
|
editorconfig: !workspace.config.editorconfig,
|
|
1977
|
-
agentMd:
|
|
2082
|
+
agentMd: writeAgentMd
|
|
1978
2083
|
});
|
|
1979
2084
|
await this.ensureAlephaConfig(root, { force });
|
|
1980
2085
|
await this.ensureMainServerTs(root, {
|
|
@@ -1982,10 +2087,15 @@ var ProjectScaffolder = class {
|
|
|
1982
2087
|
react: !!flags.react && !isExpo,
|
|
1983
2088
|
force
|
|
1984
2089
|
});
|
|
1985
|
-
if (flags.api) await this.ensureApiProject(root, {
|
|
2090
|
+
if (flags.api) await this.ensureApiProject(root, {
|
|
2091
|
+
saas: !!flags.saas,
|
|
2092
|
+
force
|
|
2093
|
+
});
|
|
1986
2094
|
if (flags.react && !isExpo) await this.ensureWebProject(root, {
|
|
1987
2095
|
api: !!flags.api,
|
|
1988
2096
|
tailwind: !!flags.tailwind,
|
|
2097
|
+
shadcn: !!flags.shadcn,
|
|
2098
|
+
saas: !!flags.saas,
|
|
1989
2099
|
force
|
|
1990
2100
|
});
|
|
1991
2101
|
}
|
|
@@ -2003,10 +2113,26 @@ var ProjectScaffolder = class {
|
|
|
2003
2113
|
root: installRoot
|
|
2004
2114
|
});
|
|
2005
2115
|
if (flags.test) await this.ensureTestDir(root);
|
|
2006
|
-
|
|
2007
|
-
|
|
2116
|
+
const exec = pmExecPrefix(pmName);
|
|
2117
|
+
if (flags.shadcn) {
|
|
2118
|
+
await run(`${exec} shadcn init --no-monorepo --base radix -t vite --yes --force --reinstall --preset ${escapeShellArg(shadcnPreset)}`, {
|
|
2119
|
+
alias: `running shadcn init (preset ${shadcnPreset})`,
|
|
2120
|
+
root
|
|
2121
|
+
});
|
|
2122
|
+
await this.fs.writeFile(this.fs.join(root, "components.json"), componentsJsonTs());
|
|
2123
|
+
}
|
|
2124
|
+
if (flags.saas) await run(`${exec} shadcn add @alepha/saas --yes --overwrite`, {
|
|
2125
|
+
alias: "adding alepha saas registry bundle",
|
|
2008
2126
|
root
|
|
2009
2127
|
});
|
|
2128
|
+
try {
|
|
2129
|
+
await run(`${pmName} run lint`, {
|
|
2130
|
+
alias: "running linter",
|
|
2131
|
+
root
|
|
2132
|
+
});
|
|
2133
|
+
} catch (err) {
|
|
2134
|
+
this.log.warn("Linter reported issues during init — continuing. Run `lint` again later to inspect.", { error: err instanceof Error ? err.message : String(err) });
|
|
2135
|
+
}
|
|
2010
2136
|
if (!workspace.isPackage) {
|
|
2011
2137
|
if (await this.ensureGitRepo(root, { force })) await run("git add .", {
|
|
2012
2138
|
alias: "staging generated files",
|
|
@@ -2045,6 +2171,30 @@ var ProjectScaffolder = class {
|
|
|
2045
2171
|
}
|
|
2046
2172
|
}
|
|
2047
2173
|
};
|
|
2174
|
+
/**
|
|
2175
|
+
* Map a package manager name to the command that runs a project-local binary.
|
|
2176
|
+
*
|
|
2177
|
+
* - npm: `npx`
|
|
2178
|
+
* - yarn: `yarn` (yarn auto-resolves binary names; `yarn shadcn ...` works)
|
|
2179
|
+
* - pnpm: `pnpm exec`
|
|
2180
|
+
* - bun: `bunx`
|
|
2181
|
+
*
|
|
2182
|
+
* Used to invoke `shadcn init` / `shadcn add` regardless of the user's PM —
|
|
2183
|
+
* `npm shadcn ...` is invalid (it tries to run a script named `shadcn`).
|
|
2184
|
+
*/
|
|
2185
|
+
/** Quote a value so it survives shell parsing. */
|
|
2186
|
+
const escapeShellArg = (value) => {
|
|
2187
|
+
if (/^[A-Za-z0-9_./@:-]+$/.test(value)) return value;
|
|
2188
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
2189
|
+
};
|
|
2190
|
+
const pmExecPrefix = (pmName) => {
|
|
2191
|
+
switch (pmName) {
|
|
2192
|
+
case "npm": return "npx";
|
|
2193
|
+
case "pnpm": return "pnpm exec";
|
|
2194
|
+
case "bun": return "bunx";
|
|
2195
|
+
default: return "yarn";
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2048
2198
|
//#endregion
|
|
2049
2199
|
//#region ../../src/cli/core/tasks/BuildTask.ts
|
|
2050
2200
|
/**
|
|
@@ -2222,6 +2372,10 @@ var BuildCloudflareTask = class BuildCloudflareTask extends BuildTask {
|
|
|
2222
2372
|
directory: "./public",
|
|
2223
2373
|
binding: "ASSETS"
|
|
2224
2374
|
};
|
|
2375
|
+
wrangler.observability ??= {
|
|
2376
|
+
enabled: true,
|
|
2377
|
+
head_sampling_rate: 1
|
|
2378
|
+
};
|
|
2225
2379
|
this.enhanceDomain(wrangler);
|
|
2226
2380
|
this.enhanceCron(ctx, wrangler);
|
|
2227
2381
|
this.enhanceDatabase(wrangler);
|
|
@@ -2456,7 +2610,7 @@ var BuildCompressTask = class extends BuildTask {
|
|
|
2456
2610
|
*
|
|
2457
2611
|
* Creates:
|
|
2458
2612
|
* - Dockerfile with configurable base image
|
|
2459
|
-
* - Copies
|
|
2613
|
+
* - Copies migrations directory if it exists
|
|
2460
2614
|
* - Builds Docker image when `--image` flag is provided
|
|
2461
2615
|
*/
|
|
2462
2616
|
var BuildDockerTask = class extends BuildTask {
|
|
@@ -2471,15 +2625,15 @@ var BuildDockerTask = class extends BuildTask {
|
|
|
2471
2625
|
await ctx.run({
|
|
2472
2626
|
name: "generate deploy config (docker)",
|
|
2473
2627
|
handler: async () => {
|
|
2474
|
-
await this.
|
|
2628
|
+
await this.copyMigrations(ctx.root, distDir);
|
|
2475
2629
|
await this.writeDockerfile(ctx.root, distDir, dockerFrom, dockerCommand);
|
|
2476
2630
|
}
|
|
2477
2631
|
});
|
|
2478
2632
|
if (ctx.flags?.image) await this.buildDockerImage(ctx, distDir);
|
|
2479
2633
|
}
|
|
2480
|
-
async
|
|
2481
|
-
const
|
|
2482
|
-
if (await this.fs.exists(
|
|
2634
|
+
async copyMigrations(root, distDir) {
|
|
2635
|
+
const migrationsDir = this.fs.join(root, "migrations");
|
|
2636
|
+
if (await this.fs.exists(migrationsDir)) await this.fs.cp(migrationsDir, this.fs.join(root, distDir, "migrations"));
|
|
2483
2637
|
}
|
|
2484
2638
|
async writeDockerfile(root, distDir, image, command) {
|
|
2485
2639
|
const dockerfile = `# This file was automatically generated. DO NOT MODIFY.
|
|
@@ -2740,6 +2894,10 @@ var BuildServerTask = class extends BuildTask {
|
|
|
2740
2894
|
chunkFileNames: "[hash].js",
|
|
2741
2895
|
assetFileNames: "[hash][extname]",
|
|
2742
2896
|
format: "esm",
|
|
2897
|
+
codeSplitting: { groups: [{
|
|
2898
|
+
name: "react",
|
|
2899
|
+
test: /node_modules\/react(\/|-dom\/)/
|
|
2900
|
+
}] },
|
|
2743
2901
|
minify: {
|
|
2744
2902
|
mangle: { keepNames: true },
|
|
2745
2903
|
compress: { keepNames: {
|
|
@@ -4280,7 +4438,12 @@ const DEFAULT_IGNORE = [
|
|
|
4280
4438
|
*/
|
|
4281
4439
|
const changelogOptions = $atom({
|
|
4282
4440
|
name: "alepha.cli.changelog.options",
|
|
4283
|
-
schema: t.object({
|
|
4441
|
+
schema: t.object({
|
|
4442
|
+
/**
|
|
4443
|
+
* Scopes to ignore (e.g., "project", "release", "chore").
|
|
4444
|
+
* Commits like `feat(chore): ...` will be excluded from changelog.
|
|
4445
|
+
*/
|
|
4446
|
+
ignore: t.optional(t.array(t.string())) }),
|
|
4284
4447
|
default: { ignore: DEFAULT_IGNORE }
|
|
4285
4448
|
});
|
|
4286
4449
|
//#endregion
|
|
@@ -4422,10 +4585,20 @@ var ChangelogCommand = class {
|
|
|
4422
4585
|
name: "changelog",
|
|
4423
4586
|
description: "Generate changelog from conventional commits (outputs to stdout)",
|
|
4424
4587
|
flags: t.object({
|
|
4588
|
+
/**
|
|
4589
|
+
* Show changes from this ref (tag, commit, branch).
|
|
4590
|
+
* Defaults to the latest version tag.
|
|
4591
|
+
* Example: --from=1.0.0
|
|
4592
|
+
*/
|
|
4425
4593
|
from: t.optional(t.string({
|
|
4426
4594
|
aliases: ["f"],
|
|
4427
4595
|
description: "Starting ref (default: latest tag)"
|
|
4428
4596
|
})),
|
|
4597
|
+
/**
|
|
4598
|
+
* Show changes up to this ref (tag, commit, branch).
|
|
4599
|
+
* Defaults to HEAD.
|
|
4600
|
+
* Example: --to=main
|
|
4601
|
+
*/
|
|
4429
4602
|
to: t.optional(t.string({
|
|
4430
4603
|
aliases: ["t"],
|
|
4431
4604
|
description: "Ending ref (default: HEAD)"
|
|
@@ -4588,6 +4761,8 @@ var InitCommand = class {
|
|
|
4588
4761
|
description: "Include React dependencies and web module (src/web/)"
|
|
4589
4762
|
})),
|
|
4590
4763
|
tailwind: t.optional(t.boolean({ description: "Include Tailwind CSS with Vite plugin. Implies --react" })),
|
|
4764
|
+
shadcn: t.optional(t.union([t.boolean(), t.text()], { description: "Set up shadcn/ui (components.json, cn helper, theme tokens, alepha registry). Pass an optional preset id (default: b0). Implies --react and --tailwind" })),
|
|
4765
|
+
saas: t.optional(t.union([t.boolean(), t.text()], { description: "Scaffold a SaaS starter: auth (login/register/reset/verify) + admin panel (/admin AppShell with users/sessions/api-keys/parameters/audits). Pass an optional preset id (default: b0). Implies --shadcn and --api" })),
|
|
4591
4766
|
test: t.optional(t.boolean({ description: "Include Vitest and create test directory" })),
|
|
4592
4767
|
force: t.optional(t.boolean({
|
|
4593
4768
|
aliases: ["f"],
|