alepha 0.15.2 → 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +68 -80
  2. package/dist/api/audits/index.d.ts +332 -332
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/files/index.d.ts +170 -170
  5. package/dist/api/files/index.d.ts.map +1 -1
  6. package/dist/api/jobs/index.d.ts +151 -151
  7. package/dist/api/keys/index.d.ts +195 -195
  8. package/dist/api/keys/index.d.ts.map +1 -1
  9. package/dist/api/parameters/index.d.ts +260 -260
  10. package/dist/api/users/index.d.ts +22 -11
  11. package/dist/api/users/index.d.ts.map +1 -1
  12. package/dist/api/users/index.js +7 -2
  13. package/dist/api/users/index.js.map +1 -1
  14. package/dist/api/verifications/index.d.ts +128 -128
  15. package/dist/api/verifications/index.d.ts.map +1 -1
  16. package/dist/bucket/index.d.ts +8 -0
  17. package/dist/bucket/index.d.ts.map +1 -1
  18. package/dist/bucket/index.js +7 -2
  19. package/dist/bucket/index.js.map +1 -1
  20. package/dist/cli/index.d.ts +191 -74
  21. package/dist/cli/index.d.ts.map +1 -1
  22. package/dist/cli/index.js +215 -48
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/command/index.d.ts +10 -0
  25. package/dist/command/index.d.ts.map +1 -1
  26. package/dist/command/index.js +67 -13
  27. package/dist/command/index.js.map +1 -1
  28. package/dist/core/index.browser.js +28 -21
  29. package/dist/core/index.browser.js.map +1 -1
  30. package/dist/core/index.d.ts.map +1 -1
  31. package/dist/core/index.js +28 -21
  32. package/dist/core/index.js.map +1 -1
  33. package/dist/core/index.native.js +28 -21
  34. package/dist/core/index.native.js.map +1 -1
  35. package/dist/email/index.d.ts +8 -0
  36. package/dist/email/index.d.ts.map +1 -1
  37. package/dist/email/index.js +7 -2
  38. package/dist/email/index.js.map +1 -1
  39. package/dist/mcp/index.d.ts +5 -5
  40. package/dist/orm/index.bun.js +32 -16
  41. package/dist/orm/index.bun.js.map +1 -1
  42. package/dist/orm/index.d.ts +4 -1
  43. package/dist/orm/index.d.ts.map +1 -1
  44. package/dist/orm/index.js +34 -22
  45. package/dist/orm/index.js.map +1 -1
  46. package/dist/react/router/index.browser.js +9 -15
  47. package/dist/react/router/index.browser.js.map +1 -1
  48. package/dist/react/router/index.d.ts +295 -407
  49. package/dist/react/router/index.d.ts.map +1 -1
  50. package/dist/react/router/index.js +566 -776
  51. package/dist/react/router/index.js.map +1 -1
  52. package/dist/redis/index.d.ts +19 -19
  53. package/dist/security/index.d.ts +42 -42
  54. package/dist/security/index.d.ts.map +1 -1
  55. package/dist/security/index.js +8 -7
  56. package/dist/security/index.js.map +1 -1
  57. package/dist/server/auth/index.d.ts +167 -167
  58. package/dist/server/core/index.d.ts +9 -9
  59. package/dist/server/health/index.d.ts +17 -17
  60. package/dist/server/links/index.d.ts +39 -39
  61. package/dist/server/static/index.js +7 -2
  62. package/dist/server/static/index.js.map +1 -1
  63. package/dist/server/swagger/index.d.ts +8 -0
  64. package/dist/server/swagger/index.d.ts.map +1 -1
  65. package/dist/server/swagger/index.js +7 -2
  66. package/dist/server/swagger/index.js.map +1 -1
  67. package/dist/sms/index.d.ts +8 -0
  68. package/dist/sms/index.d.ts.map +1 -1
  69. package/dist/sms/index.js +7 -2
  70. package/dist/sms/index.js.map +1 -1
  71. package/dist/system/index.browser.js +734 -12
  72. package/dist/system/index.browser.js.map +1 -1
  73. package/dist/system/index.d.ts +8 -0
  74. package/dist/system/index.d.ts.map +1 -1
  75. package/dist/system/index.js +7 -2
  76. package/dist/system/index.js.map +1 -1
  77. package/dist/vite/index.d.ts +1 -1
  78. package/dist/vite/index.js +15 -7
  79. package/dist/vite/index.js.map +1 -1
  80. package/package.json +4 -2
  81. package/src/api/logs/TODO.md +13 -10
  82. package/src/cli/apps/AlephaPackageBuilderCli.ts +9 -0
  83. package/src/cli/atoms/buildOptions.ts +99 -9
  84. package/src/cli/commands/build.ts +149 -32
  85. package/src/cli/commands/db.ts +5 -7
  86. package/src/cli/commands/init.spec.ts +50 -6
  87. package/src/cli/commands/init.ts +28 -5
  88. package/src/cli/providers/ViteDevServerProvider.ts +1 -10
  89. package/src/cli/services/AlephaCliUtils.ts +16 -0
  90. package/src/cli/services/PackageManagerUtils.ts +2 -0
  91. package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
  92. package/src/cli/services/ProjectScaffolder.ts +28 -6
  93. package/src/cli/templates/agentMd.ts +6 -1
  94. package/src/cli/templates/apiAppSecurityTs.ts +11 -0
  95. package/src/cli/templates/apiIndexTs.ts +18 -4
  96. package/src/cli/templates/webAppRouterTs.ts +25 -1
  97. package/src/cli/templates/webHelloComponentTsx.ts +15 -5
  98. package/src/command/helpers/Runner.spec.ts +135 -0
  99. package/src/command/helpers/Runner.ts +4 -1
  100. package/src/command/providers/CliProvider.spec.ts +325 -0
  101. package/src/command/providers/CliProvider.ts +117 -7
  102. package/src/core/Alepha.ts +32 -25
  103. package/src/orm/index.bun.ts +1 -1
  104. package/src/orm/index.ts +2 -6
  105. package/src/orm/providers/drivers/BunSqliteProvider.ts +4 -1
  106. package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
  107. package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
  108. package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
  109. package/src/react/router/hooks/useActive.ts +1 -1
  110. package/src/react/router/hooks/useRouter.ts +1 -1
  111. package/src/react/router/index.ts +4 -0
  112. package/src/react/router/primitives/$page.browser.spec.tsx +24 -24
  113. package/src/react/router/primitives/$page.spec.tsx +0 -32
  114. package/src/react/router/primitives/$page.ts +6 -14
  115. package/src/react/router/providers/ReactBrowserProvider.ts +6 -3
  116. package/src/react/router/providers/ReactPageProvider.ts +1 -1
  117. package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
  118. package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
  119. package/src/react/router/providers/ReactServerProvider.ts +7 -78
  120. package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
  121. package/src/react/router/providers/ReactServerTemplateProvider.ts +228 -665
  122. package/src/react/router/services/ReactRouter.ts +13 -13
  123. package/src/security/__tests__/ServerSecurityProvider.spec.ts +77 -0
  124. package/src/security/providers/ServerSecurityProvider.ts +30 -22
  125. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
  126. package/src/system/index.browser.ts +25 -0
  127. package/src/system/index.workerd.ts +1 -0
  128. package/src/system/providers/FileSystemProvider.ts +8 -0
  129. package/src/system/providers/NodeFileSystemProvider.ts +11 -2
  130. package/src/vite/tasks/buildServer.ts +2 -12
  131. package/src/vite/tasks/generateCloudflare.ts +10 -7
  132. package/src/vite/tasks/generateDocker.ts +4 -0
package/dist/cli/index.js CHANGED
@@ -1370,8 +1370,13 @@ var NodeFileSystemProvider = class {
1370
1370
  * await fs.mkdir("/tmp/mydir", { mode: 0o755 });
1371
1371
  * ```
1372
1372
  */
1373
- async mkdir(path, options) {
1374
- await mkdir(path, options);
1373
+ async mkdir(path, options = {}) {
1374
+ const p = mkdir(path, {
1375
+ recursive: options.recursive ?? true,
1376
+ mode: options.mode
1377
+ });
1378
+ if (options.force === false) await p;
1379
+ else await p.catch(() => {});
1375
1380
  }
1376
1381
  /**
1377
1382
  * Lists files in a directory.
@@ -1912,13 +1917,24 @@ $atom$1[KIND] = "atom";
1912
1917
  * Build options atom for CLI build command.
1913
1918
  *
1914
1919
  * Defines the available build configuration options with their defaults.
1915
- * Options can be overridden via vite.config.ts or CLI flags.
1920
+ * Options can be overridden via alepha.config.ts or CLI flags.
1916
1921
  */
1917
1922
  const buildOptions = $atom$1({
1918
1923
  name: "alepha.cli.build.options",
1919
1924
  description: "Build configuration options",
1920
1925
  schema: t.object({
1921
1926
  stats: t.optional(t.boolean({ default: false })),
1927
+ target: t.optional(t.enum([
1928
+ "bare",
1929
+ "docker",
1930
+ "vercel",
1931
+ "cloudflare"
1932
+ ])),
1933
+ runtime: t.optional(t.enum([
1934
+ "node",
1935
+ "bun",
1936
+ "workerd"
1937
+ ])),
1922
1938
  vercel: t.optional(t.object({
1923
1939
  projectName: t.optional(t.string()),
1924
1940
  orgId: t.optional(t.string()),
@@ -1930,8 +1946,13 @@ const buildOptions = $atom$1({
1930
1946
  })),
1931
1947
  cloudflare: t.optional(t.object({ config: t.optional(t.json()) })),
1932
1948
  docker: t.optional(t.object({
1933
- image: t.optional(t.string({ default: "node:24-alpine" })),
1934
- command: t.optional(t.string({ default: "node" }))
1949
+ from: t.optional(t.string()),
1950
+ command: t.optional(t.string()),
1951
+ image: t.optional(t.object({
1952
+ tag: t.string(),
1953
+ args: t.optional(t.string()),
1954
+ oci: t.optional(t.boolean())
1955
+ }))
1935
1956
  })),
1936
1957
  sitemap: t.optional(t.object({ hostname: t.string() }))
1937
1958
  }),
@@ -2205,6 +2226,18 @@ ${models.map((it) => `export const ${it} = models["${it}"];`).join("\n")}
2205
2226
  isInstalledAsync(cmd) {
2206
2227
  return this.shell.isInstalled(cmd);
2207
2228
  }
2229
+ /**
2230
+ * Get the current git revision (commit SHA).
2231
+ *
2232
+ * @returns The short commit SHA or "unknown" if not in a git repo
2233
+ */
2234
+ async getGitRevision() {
2235
+ try {
2236
+ return (await this.shell.run("git rev-parse --short HEAD", { capture: true })).trim();
2237
+ } catch {
2238
+ return "unknown";
2239
+ }
2240
+ }
2208
2241
  };
2209
2242
 
2210
2243
  //#endregion
@@ -2220,6 +2253,7 @@ var devDependencies = {
2220
2253
  "@types/react": "^19.2.10",
2221
2254
  "@types/react-dom": "^19.2.3",
2222
2255
  "@types/ws": "^8.18.1",
2256
+ "@vitejs/plugin-react": "^5.1.2",
2223
2257
  "cron-schedule": "^6.0.0",
2224
2258
  "jose": "^6.1.3",
2225
2259
  "jsdom": "^27.4.0",
@@ -2477,6 +2511,7 @@ var PackageManagerUtils = class {
2477
2511
  if (modes.react) {
2478
2512
  dependencies.react = alephaDeps.react;
2479
2513
  dependencies["react-dom"] = alephaDeps["react-dom"];
2514
+ devDependencies$1["@vitejs/plugin-react"] = alephaDeps["@vitejs/plugin-react"];
2480
2515
  devDependencies$1["@types/react"] = alephaDeps["@types/react"];
2481
2516
  }
2482
2517
  return {
@@ -2783,7 +2818,26 @@ alepha build # Build the project
2783
2818
  ## Source Code Access
2784
2819
 
2785
2820
  Full framework source available at \`node_modules/alepha/src/\`.
2786
- Read primitives directly when you need implementation details.
2821
+
2822
+ **IMPORTANT:** When answering questions about Alepha primitives, APIs, or internals:
2823
+ 1. ALWAYS read the local source code first at \`node_modules/alepha/src/\` or \`node_modules/@alepha/ui/src/\` for UI-related questions
2824
+ 2. Use \`Glob\` to find relevant files: \`node_modules/alepha/src/**/primitives/$<name>.ts\`
2825
+ 3. Read the implementation AND the \`.spec.ts\` test files for usage examples
2826
+ 4. Use external documentation as a fallback if source code is insufficient
2827
+ `.trim();
2828
+ };
2829
+
2830
+ //#endregion
2831
+ //#region ../../src/cli/templates/apiAppSecurityTs.ts
2832
+ const apiAppSecurityTs = () => {
2833
+ return `
2834
+ import { $realm } from "alepha/api/users";
2835
+
2836
+ export class AppSecurity {
2837
+ users = $realm({
2838
+ // configure your realm here
2839
+ });
2840
+ }
2787
2841
  `.trim();
2788
2842
  };
2789
2843
 
@@ -2811,14 +2865,21 @@ export class HelloController {
2811
2865
  //#endregion
2812
2866
  //#region ../../src/cli/templates/apiIndexTs.ts
2813
2867
  const apiIndexTs = (options = {}) => {
2814
- const { appName = "app" } = options;
2868
+ const { appName = "app", auth = false } = options;
2869
+ const imports = ["import { $module } from \"alepha\";"];
2870
+ const services = [];
2871
+ if (auth) {
2872
+ imports.push("import { AppSecurity } from \"./AppSecurity.ts\";");
2873
+ services.push("AppSecurity");
2874
+ }
2875
+ imports.push("import { HelloController } from \"./controllers/HelloController.ts\";");
2876
+ services.push("HelloController");
2815
2877
  return `
2816
- import { $module } from "alepha";
2817
- import { HelloController } from "./controllers/HelloController.ts";
2878
+ ${imports.join("\n")}
2818
2879
 
2819
2880
  export const ApiModule = $module({
2820
2881
  name: "${appName}.api",
2821
- services: [HelloController],
2882
+ services: [${services.join(", ")}],
2822
2883
  });
2823
2884
  `.trim();
2824
2885
  };
@@ -3006,6 +3067,8 @@ const webAppRouterTs = (options) => {
3006
3067
  const imports = [];
3007
3068
  const classMembers = [];
3008
3069
  if (options.ui) imports.push("import { $ui } from \"@alepha/ui\";");
3070
+ if (options.auth) imports.push("import { $uiAuth } from \"@alepha/ui/auth\";");
3071
+ if (options.admin) imports.push("import { $uiAdmin } from \"@alepha/ui/admin\";");
3009
3072
  imports.push("import { $page } from \"alepha/react/router\";");
3010
3073
  if (options.api) {
3011
3074
  imports.push("import { $client } from \"alepha/server/links\";");
@@ -3014,6 +3077,8 @@ const webAppRouterTs = (options) => {
3014
3077
  }
3015
3078
  if (options.ui) {
3016
3079
  classMembers.push(" ui = $ui();");
3080
+ if (options.auth) classMembers.push(" uiAuth = $uiAuth();");
3081
+ if (options.admin) classMembers.push(" uiAdmin = $uiAdmin();");
3017
3082
  classMembers.push(` layout = $page({
3018
3083
  parent: this.ui.root,
3019
3084
  children: () => [this.home],
@@ -3037,7 +3102,12 @@ ${classMembers.join("\n\n")}
3037
3102
 
3038
3103
  //#endregion
3039
3104
  //#region ../../src/cli/templates/webHelloComponentTsx.ts
3040
- const webHelloComponentTsx = () => `import { useState } from "react";
3105
+ const webHelloComponentTsx = (options = {}) => {
3106
+ const imports = [];
3107
+ if (options.auth) imports.push("import { UserButton } from \"@alepha/ui/auth\";");
3108
+ imports.push("import { useState } from \"react\";");
3109
+ const userButton = options.auth ? "\n <UserButton />" : "";
3110
+ return `${imports.join("\n")}
3041
3111
 
3042
3112
  interface Props {
3043
3113
  message?: string;
@@ -3048,14 +3118,15 @@ const Hello = (props: Props) => {
3048
3118
  return (
3049
3119
  <div>
3050
3120
  <h1>{message}</h1>
3051
- <input value={message} onChange={e => setMessage(e.target.value)} />
3052
- <p>Edit this component in src/web/components/Hello.tsx</p>
3121
+ <input value={message} onChange={(e) => setMessage(e.target.value)} />
3122
+ <p>Edit this component in src/web/components/Hello.tsx</p>${userButton}
3053
3123
  </div>
3054
3124
  );
3055
3125
  };
3056
3126
 
3057
3127
  export default Hello;
3058
- `.trim();
3128
+ `;
3129
+ };
3059
3130
 
3060
3131
  //#endregion
3061
3132
  //#region ../../src/cli/templates/webIndexTs.ts
@@ -3097,7 +3168,7 @@ var ProjectScaffolder = class {
3097
3168
  * - Falls back to "app" if empty
3098
3169
  */
3099
3170
  getAppName(root) {
3100
- return basename(root).toLowerCase().replace(/[\s\-_]/g, "") || "app";
3171
+ return basename(root).toLowerCase().replace(/[\s\-_.\d]/g, "") || "app";
3101
3172
  }
3102
3173
  /**
3103
3174
  * Ensure all configuration files exist.
@@ -3175,8 +3246,12 @@ var ProjectScaffolder = class {
3175
3246
  async ensureApiProject(root, opts = {}) {
3176
3247
  const appName = this.getAppName(root);
3177
3248
  await this.fs.mkdir(this.fs.join(root, "src/api/controllers"), { recursive: true });
3178
- await this.ensureFile(root, "src/api/index.ts", apiIndexTs({ appName }), opts.force);
3249
+ await this.ensureFile(root, "src/api/index.ts", apiIndexTs({
3250
+ appName,
3251
+ auth: opts.auth
3252
+ }), opts.force);
3179
3253
  await this.ensureFile(root, "src/api/controllers/HelloController.ts", apiHelloControllerTs(), opts.force);
3254
+ if (opts.auth) await this.ensureFile(root, "src/api/AppSecurity.ts", apiAppSecurityTs(), opts.force);
3180
3255
  }
3181
3256
  /**
3182
3257
  * Ensure web/React project structure exists.
@@ -3193,9 +3268,11 @@ var ProjectScaffolder = class {
3193
3268
  await this.ensureFile(root, "src/web/index.ts", webIndexTs({ appName }), opts.force);
3194
3269
  await this.ensureFile(root, "src/web/AppRouter.ts", webAppRouterTs({
3195
3270
  api: opts.api,
3196
- ui: opts.ui
3271
+ ui: opts.ui,
3272
+ auth: opts.auth,
3273
+ admin: opts.admin
3197
3274
  }), opts.force);
3198
- await this.ensureFile(root, "src/web/components/Hello.tsx", webHelloComponentTsx(), opts.force);
3275
+ await this.ensureFile(root, "src/web/components/Hello.tsx", webHelloComponentTsx({ auth: opts.auth }), opts.force);
3199
3276
  await this.ensureFile(root, "src/main.browser.ts", mainBrowserTs(), opts.force);
3200
3277
  }
3201
3278
  /**
@@ -3243,17 +3320,55 @@ var BuildCommand = class {
3243
3320
  boot = $inject(AppEntryProvider);
3244
3321
  viteBuildProvider = $inject(ViteBuildProvider);
3245
3322
  options = $use(buildOptions);
3323
+ /**
3324
+ * Resolve the effective runtime based on target and explicit runtime flag.
3325
+ *
3326
+ * Some targets force a specific runtime:
3327
+ * - `cloudflare` always uses `workerd`
3328
+ * - `vercel` always uses `node`
3329
+ * - `docker` and bare deployments respect the runtime flag
3330
+ *
3331
+ * @throws {AlephaError} If an incompatible runtime is specified for a target
3332
+ */
3333
+ resolveRuntime(target, runtime) {
3334
+ if (target === "cloudflare") {
3335
+ if (runtime && runtime !== "workerd") throw new AlephaError(`Target 'cloudflare' requires 'workerd' runtime, got '${runtime}'`);
3336
+ return "workerd";
3337
+ }
3338
+ if (target === "vercel") {
3339
+ if (runtime && runtime !== "node") throw new AlephaError(`Target 'vercel' requires 'node' runtime, got '${runtime}'`);
3340
+ return "node";
3341
+ }
3342
+ return runtime ?? "node";
3343
+ }
3246
3344
  build = $command({
3247
3345
  name: "build",
3248
3346
  mode: "production",
3249
3347
  description: "Build the project for production",
3250
3348
  flags: t.object({
3251
3349
  stats: t.optional(t.boolean({ description: "Generate build stats report" })),
3252
- vercel: t.optional(t.boolean({ description: "Generate Vercel deployment configuration" })),
3253
- cloudflare: t.optional(t.boolean({ description: "Generate Cloudflare Workers configuration" })),
3254
- docker: t.optional(t.boolean({ description: "Generate Docker configuration" })),
3255
- sitemap: t.optional(t.text({ description: "Generate sitemap.xml with base URL" })),
3256
- bun: t.optional(t.boolean({ description: "Prioritize .bun.ts entry files for Bun runtime" }))
3350
+ target: t.optional(t.enum([
3351
+ "bare",
3352
+ "docker",
3353
+ "vercel",
3354
+ "cloudflare"
3355
+ ], {
3356
+ aliases: ["t"],
3357
+ description: "Deployment target"
3358
+ })),
3359
+ runtime: t.optional(t.enum([
3360
+ "node",
3361
+ "bun",
3362
+ "workerd"
3363
+ ], {
3364
+ aliases: ["r"],
3365
+ description: "JavaScript runtime"
3366
+ })),
3367
+ image: t.optional(t.union([t.boolean(), t.text()], {
3368
+ aliases: ["i"],
3369
+ description: "Build Docker image. Use -i for latest, -i=<version> for specific version"
3370
+ })),
3371
+ sitemap: t.optional(t.text({ description: "Generate sitemap.xml with base URL" }))
3257
3372
  }),
3258
3373
  handler: async ({ flags, run, root }) => {
3259
3374
  process.env.NODE_ENV = "production";
@@ -3266,6 +3381,13 @@ var BuildCommand = class {
3266
3381
  await run.rm("dist", { alias: "clean dist" });
3267
3382
  const options = this.options;
3268
3383
  await this.utils.loadEnv(root, [".env", ".env.production"]);
3384
+ const target = flags.target ?? options.target;
3385
+ const runtime = this.resolveRuntime(target, flags.runtime ?? options.runtime);
3386
+ if (flags.image && target !== "docker") throw new AlephaError(`Flag '--image' requires '--target=docker', got '${target ?? "bare"}'`);
3387
+ this.log.trace("Build configuration", {
3388
+ target,
3389
+ runtime
3390
+ });
3269
3391
  const stats = flags.stats ?? options.stats ?? false;
3270
3392
  let template = "";
3271
3393
  let hasClient = false;
@@ -3303,8 +3425,8 @@ var BuildCommand = class {
3303
3425
  const clientIndexPath = `${distDir}/${publicDir}/index.html`;
3304
3426
  const clientBuilt = await this.fs.exists(clientIndexPath);
3305
3427
  const conditions = [];
3306
- if (flags.bun) conditions.push("bun");
3307
- if (options.cloudflare) conditions.push("workerd");
3428
+ if (runtime === "bun") conditions.push("bun");
3429
+ else if (runtime === "workerd") conditions.push("workerd");
3308
3430
  await buildServer({
3309
3431
  silent: true,
3310
3432
  entry: entry.server,
@@ -3339,7 +3461,7 @@ var BuildCommand = class {
3339
3461
  run
3340
3462
  });
3341
3463
  }
3342
- if (flags.vercel || options.vercel) await run({
3464
+ if (target === "vercel") await run({
3343
3465
  name: "add Vercel config",
3344
3466
  handler: () => generateVercel({
3345
3467
  distDir,
@@ -3347,20 +3469,56 @@ var BuildCommand = class {
3347
3469
  config: options.vercel
3348
3470
  })
3349
3471
  });
3350
- if (flags.cloudflare || options.cloudflare) await run({
3472
+ if (target === "cloudflare") await run({
3351
3473
  name: "add Cloudflare config",
3352
3474
  handler: () => generateCloudflare({
3353
3475
  distDir,
3354
3476
  config: options.cloudflare?.config
3355
3477
  })
3356
3478
  });
3357
- if (flags.docker || options.docker) await run({
3358
- name: "add Docker config",
3359
- handler: () => generateDocker({
3360
- distDir,
3361
- ...options.docker
3362
- })
3363
- });
3479
+ if (target === "docker") {
3480
+ const dockerFrom = options.docker?.from ?? (runtime === "bun" ? "oven/bun:alpine" : "node:24-alpine");
3481
+ const dockerCommand = options.docker?.command ?? (runtime === "bun" ? "bun" : "node");
3482
+ await run({
3483
+ name: "add Docker config",
3484
+ handler: () => generateDocker({
3485
+ distDir,
3486
+ image: dockerFrom,
3487
+ command: dockerCommand
3488
+ })
3489
+ });
3490
+ if (flags.image) {
3491
+ const imageConfig = options.docker?.image;
3492
+ const flagValue = typeof flags.image === "string" ? flags.image : null;
3493
+ let imageTag;
3494
+ let version;
3495
+ if (!flagValue) {
3496
+ if (!imageConfig?.tag) throw new AlephaError("Flag '--image' requires 'build.docker.image.tag' in config");
3497
+ version = "latest";
3498
+ imageTag = `${imageConfig.tag}:${version}`;
3499
+ } else if (flagValue.startsWith(":")) {
3500
+ if (!imageConfig?.tag) throw new AlephaError("Flag '--image=:version' requires 'build.docker.image.tag' in config");
3501
+ version = flagValue.slice(1);
3502
+ imageTag = `${imageConfig.tag}:${version}`;
3503
+ } else if (flagValue.includes(":")) {
3504
+ imageTag = flagValue;
3505
+ version = flagValue.split(":")[1];
3506
+ } else {
3507
+ imageTag = `${flagValue}:latest`;
3508
+ version = "latest";
3509
+ }
3510
+ const args = [];
3511
+ if (imageConfig?.args) args.push(imageConfig.args);
3512
+ if (imageConfig?.oci) {
3513
+ const revision = await this.utils.getGitRevision();
3514
+ const created = (/* @__PURE__ */ new Date()).toISOString();
3515
+ args.push(`--label "org.opencontainers.image.revision=${revision}"`);
3516
+ args.push(`--label "org.opencontainers.image.created=${created}"`);
3517
+ args.push(`--label "org.opencontainers.image.version=${version}"`);
3518
+ }
3519
+ await run(`docker build ${args.length > 0 ? `${args.join(" ")} ` : ""}-t ${imageTag} ${distDir}`, { alias: `docker build ${imageTag}` });
3520
+ }
3521
+ }
3364
3522
  }
3365
3523
  });
3366
3524
  };
@@ -3629,9 +3787,9 @@ var DbCommand = class {
3629
3787
  const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
3630
3788
  if (!accountId) throw new AlephaError("CLOUDFLARE_ACCOUNT_ID environment variable is not set. https://orm.drizzle.team/docs/guides/d1-http-with-drizzle-kit");
3631
3789
  const url = options.providerUrl;
3632
- if (!url.startsWith("cloudflare-d1://")) throw new AlephaError("D1 provider URL must start with 'cloudflare-d1://'.");
3633
- const [, databaseId] = url.replace("cloudflare-d1://", "").replace("cloudflare-d1:", "").split(":");
3634
- if (!databaseId) throw new AlephaError("Database ID is missing in the D1 provider URL. Cloudflare D1 URL format: cloudflare-d1://<database_name>:<database_id>");
3790
+ if (!url.startsWith("d1://")) throw new AlephaError("D1 provider URL must start with 'd1://'.");
3791
+ const [, databaseId] = url.replace("d1://", "").replace("d1:", "").split(":");
3792
+ if (!databaseId) throw new AlephaError("Database ID is missing in the D1 provider URL. Cloudflare D1 URL format: d1://<database_name>:<database_id>");
3635
3793
  config.dbCredentials = {
3636
3794
  accountId,
3637
3795
  databaseId,
@@ -3763,7 +3921,6 @@ var DeployCommand = class {
3763
3921
  var ViteDevServerProvider = class {
3764
3922
  log = $logger();
3765
3923
  fs = $inject(FileSystemProvider);
3766
- templateProvider = $inject(ViteUtils);
3767
3924
  server;
3768
3925
  options;
3769
3926
  alepha = null;
@@ -3905,12 +4062,10 @@ var ViteDevServerProvider = class {
3905
4062
  }
3906
4063
  }
3907
4064
  /**
3908
- * Setup Alepha instance with Vite middleware and template.
4065
+ * Setup Alepha instance with Vite middleware.
3909
4066
  */
3910
4067
  async setupAlepha() {
3911
4068
  if (!this.alepha || !this.hasReact()) return;
3912
- const template = await this.server.transformIndexHtml("/", this.templateProvider.generateIndexHtml(this.options.entry));
3913
- this.alepha.store.set("alepha.react.server.template", template);
3914
4069
  this.alepha.events.on("server:onRequest", {
3915
4070
  priority: "first",
3916
4071
  callback: async ({ request }) => {
@@ -4350,10 +4505,7 @@ var InitCommand = class {
4350
4505
  lowercase: true
4351
4506
  })),
4352
4507
  flags: t.object({
4353
- agent: t.optional(t.boolean({
4354
- aliases: ["a"],
4355
- description: "Add AI agent instructions (CLAUDE.md if claude CLI installed, else AGENTS.md)"
4356
- })),
4508
+ ai: t.optional(t.boolean({ description: "Add AI agent instructions (CLAUDE.md if claude CLI installed, else AGENTS.md)" })),
4357
4509
  pm: t.optional(t.enum([
4358
4510
  "yarn",
4359
4511
  "npm",
@@ -4366,6 +4518,8 @@ var InitCommand = class {
4366
4518
  description: "Include React dependencies and web module (src/web/)"
4367
4519
  })),
4368
4520
  ui: t.optional(t.boolean({ description: "Include @alepha/ui (components, auth portal, admin portal)" })),
4521
+ auth: t.optional(t.boolean({ description: "Include authentication (AppSecurity, $uiAuth). Implies --api --ui --react" })),
4522
+ admin: t.optional(t.boolean({ description: "Include admin portal ($uiAdmin). Implies --auth" })),
4369
4523
  test: t.optional(t.boolean({ description: "Include Vitest and create test directory" })),
4370
4524
  force: t.optional(t.boolean({
4371
4525
  aliases: ["f"],
@@ -4375,12 +4529,17 @@ var InitCommand = class {
4375
4529
  handler: async ({ run, flags, root, args }) => {
4376
4530
  if (args) {
4377
4531
  root = this.fs.join(root, args);
4378
- await this.fs.mkdir(root);
4532
+ await this.fs.mkdir(root, { force: true });
4533
+ }
4534
+ if (flags.admin) flags.auth = true;
4535
+ if (flags.auth) {
4536
+ flags.api = true;
4537
+ flags.ui = true;
4379
4538
  }
4380
4539
  if (flags.ui) flags.react = true;
4381
4540
  const workspace = await this.pm.getWorkspaceContext(root);
4382
4541
  let agentType = false;
4383
- if (flags.agent) agentType = await this.utils.isInstalledAsync("claude") ? "claude" : "agents";
4542
+ if (flags.ai) agentType = await this.utils.isInstalledAsync("claude") ? "claude" : "agents";
4384
4543
  const isExpo = await this.pm.hasExpo(root);
4385
4544
  const force = !!flags.force;
4386
4545
  await run({
@@ -4406,10 +4565,15 @@ var InitCommand = class {
4406
4565
  react: !!flags.react && !isExpo,
4407
4566
  force
4408
4567
  });
4409
- if (flags.api) await this.scaffolder.ensureApiProject(root, { force });
4568
+ if (flags.api) await this.scaffolder.ensureApiProject(root, {
4569
+ auth: !!flags.auth,
4570
+ force
4571
+ });
4410
4572
  if (flags.react && !isExpo) await this.scaffolder.ensureWebProject(root, {
4411
4573
  api: !!flags.api,
4412
4574
  ui: !!flags.ui,
4575
+ auth: !!flags.auth,
4576
+ admin: !!flags.admin,
4413
4577
  force
4414
4578
  });
4415
4579
  }
@@ -4648,6 +4812,7 @@ var AlephaPackageBuilderCli = class {
4648
4812
  pkgData.exports[path].types = `./src/${item.name}/index.ts`;
4649
4813
  if (item.native) pkgData.exports[path]["react-native"] = `./src/${item.name}/index.native.ts`;
4650
4814
  else if (item.browser) pkgData.exports[path]["react-native"] = `./src/${item.name}/index.browser.ts`;
4815
+ if (item.workerd) pkgData.exports[path].workerd = `./src/${item.name}/index.workerd.ts`;
4651
4816
  if (item.browser) pkgData.exports[path].browser = `./src/${item.name}/index.browser.ts`;
4652
4817
  if (item.bun) pkgData.exports[path].bun = `./src/${item.name}/index.bun.ts`;
4653
4818
  pkgData.exports[path].import = `./src/${item.name}/index.ts`;
@@ -4804,6 +4969,7 @@ async function analyzeModules(srcDir, packageName) {
4804
4969
  const hasNative = await fileExists(join(modulePath, "index.native.ts"));
4805
4970
  const hasBun = await fileExists(join(modulePath, "index.bun.ts"));
4806
4971
  const hasNode = await fileExists(join(modulePath, "index.node.ts"));
4972
+ const hasEdge = await fileExists(join(modulePath, "index.workerd.ts"));
4807
4973
  const files = await getAllFiles(modulePath);
4808
4974
  for (const file of files) {
4809
4975
  const deps = extractAlephaDependencies(await readFile(file, "utf-8"), packageName, moduleName);
@@ -4818,6 +4984,7 @@ async function analyzeModules(srcDir, packageName) {
4818
4984
  dependencies: Array.from(dependencies)
4819
4985
  };
4820
4986
  if (hasNative) module.native = true;
4987
+ if (hasEdge) module.workerd = true;
4821
4988
  if (hasBrowser) module.browser = true;
4822
4989
  if (hasBun) module.bun = true;
4823
4990
  if (hasNode) module.node = true;