alepha 0.13.8 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/api/audits/index.d.ts +2 -1
  2. package/dist/api/audits/index.d.ts.map +1 -0
  3. package/dist/api/files/index.d.ts +2 -1
  4. package/dist/api/files/index.d.ts.map +1 -0
  5. package/dist/api/jobs/index.d.ts +158 -157
  6. package/dist/api/jobs/index.d.ts.map +1 -0
  7. package/dist/api/notifications/index.d.ts.map +1 -0
  8. package/dist/api/parameters/index.d.ts +4 -4
  9. package/dist/api/parameters/index.d.ts.map +1 -0
  10. package/dist/api/users/index.d.ts +132 -131
  11. package/dist/api/users/index.d.ts.map +1 -0
  12. package/dist/api/verifications/index.d.ts.map +1 -0
  13. package/dist/batch/index.d.ts.map +1 -0
  14. package/dist/bucket/index.d.ts.map +1 -0
  15. package/dist/cache/core/index.d.ts.map +1 -0
  16. package/dist/cache/redis/index.d.ts.map +1 -0
  17. package/dist/cli/index.d.ts +44 -32
  18. package/dist/cli/index.d.ts.map +1 -0
  19. package/dist/cli/index.js +380 -109
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/command/index.d.ts +11 -1
  22. package/dist/command/index.d.ts.map +1 -0
  23. package/dist/command/index.js +45 -6
  24. package/dist/command/index.js.map +1 -1
  25. package/dist/core/index.browser.js +1334 -1318
  26. package/dist/core/index.browser.js.map +1 -1
  27. package/dist/core/index.d.ts +75 -71
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +1337 -1321
  30. package/dist/core/index.js.map +1 -1
  31. package/dist/core/index.native.js +1337 -1321
  32. package/dist/core/index.native.js.map +1 -1
  33. package/dist/datetime/index.d.ts.map +1 -0
  34. package/dist/email/index.d.ts.map +1 -0
  35. package/dist/fake/index.d.ts.map +1 -0
  36. package/dist/file/index.d.ts.map +1 -0
  37. package/dist/lock/core/index.d.ts.map +1 -0
  38. package/dist/lock/redis/index.d.ts.map +1 -0
  39. package/dist/logger/index.d.ts +1 -0
  40. package/dist/logger/index.d.ts.map +1 -0
  41. package/dist/mcp/index.d.ts +820 -0
  42. package/dist/mcp/index.d.ts.map +1 -0
  43. package/dist/mcp/index.js +978 -0
  44. package/dist/mcp/index.js.map +1 -0
  45. package/dist/orm/index.d.ts +180 -107
  46. package/dist/orm/index.d.ts.map +1 -0
  47. package/dist/orm/index.js +260 -174
  48. package/dist/orm/index.js.map +1 -1
  49. package/dist/queue/core/index.d.ts +4 -4
  50. package/dist/queue/core/index.d.ts.map +1 -0
  51. package/dist/queue/redis/index.d.ts.map +1 -0
  52. package/dist/redis/index.d.ts.map +1 -0
  53. package/dist/retry/index.d.ts.map +1 -0
  54. package/dist/router/index.d.ts.map +1 -0
  55. package/dist/scheduler/index.d.ts.map +1 -0
  56. package/dist/security/index.d.ts.map +1 -0
  57. package/dist/server/auth/index.d.ts +155 -155
  58. package/dist/server/auth/index.d.ts.map +1 -0
  59. package/dist/server/cache/index.d.ts.map +1 -0
  60. package/dist/server/compress/index.d.ts.map +1 -0
  61. package/dist/server/cookies/index.d.ts.map +1 -0
  62. package/dist/server/core/index.d.ts.map +1 -0
  63. package/dist/server/cors/index.d.ts.map +1 -0
  64. package/dist/server/health/index.d.ts.map +1 -0
  65. package/dist/server/helmet/index.d.ts.map +1 -0
  66. package/dist/server/links/index.d.ts +33 -33
  67. package/dist/server/links/index.d.ts.map +1 -0
  68. package/dist/server/metrics/index.d.ts.map +1 -0
  69. package/dist/server/multipart/index.d.ts.map +1 -0
  70. package/dist/server/proxy/index.d.ts.map +1 -0
  71. package/dist/server/rate-limit/index.d.ts.map +1 -0
  72. package/dist/server/security/index.d.ts +9 -9
  73. package/dist/server/security/index.d.ts.map +1 -0
  74. package/dist/server/static/index.d.ts.map +1 -0
  75. package/dist/server/swagger/index.d.ts.map +1 -0
  76. package/dist/sms/index.d.ts.map +1 -0
  77. package/dist/thread/index.d.ts.map +1 -0
  78. package/dist/topic/core/index.d.ts.map +1 -0
  79. package/dist/topic/redis/index.d.ts.map +1 -0
  80. package/dist/vite/index.d.ts +10 -2
  81. package/dist/vite/index.d.ts.map +1 -0
  82. package/dist/vite/index.js +36 -14
  83. package/dist/vite/index.js.map +1 -1
  84. package/dist/websocket/index.d.ts.map +1 -0
  85. package/package.json +9 -4
  86. package/src/cli/apps/AlephaCli.ts +2 -0
  87. package/src/cli/apps/AlephaPackageBuilderCli.ts +12 -8
  88. package/src/cli/assets/mainTs.ts +9 -10
  89. package/src/cli/commands/ChangelogCommands.ts +389 -0
  90. package/src/cli/commands/DrizzleCommands.ts +204 -4
  91. package/src/cli/commands/ViteCommands.ts +26 -16
  92. package/src/cli/services/AlephaCliUtils.ts +23 -150
  93. package/src/command/providers/CliProvider.ts +76 -5
  94. package/src/core/providers/SchemaValidator.ts +23 -1
  95. package/src/mcp/errors/McpError.ts +72 -0
  96. package/src/mcp/helpers/jsonrpc.ts +163 -0
  97. package/src/mcp/index.ts +132 -0
  98. package/src/mcp/interfaces/McpTypes.ts +248 -0
  99. package/src/mcp/primitives/$prompt.ts +188 -0
  100. package/src/mcp/primitives/$resource.ts +171 -0
  101. package/src/mcp/primitives/$tool.ts +285 -0
  102. package/src/mcp/providers/McpServerProvider.ts +382 -0
  103. package/src/mcp/transports/SseMcpTransport.ts +172 -0
  104. package/src/mcp/transports/StdioMcpTransport.ts +126 -0
  105. package/src/orm/index.ts +12 -0
  106. package/src/orm/providers/drivers/CloudflareD1Provider.ts +164 -0
  107. package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -1
  108. package/src/vite/plugins/viteAlephaBuild.ts +8 -2
  109. package/src/vite/plugins/viteAlephaDev.ts +6 -2
  110. package/src/vite/tasks/buildServer.ts +1 -1
  111. package/src/vite/tasks/generateCloudflare.ts +43 -15
  112. package/src/vite/tasks/runAlepha.ts +1 -0
@@ -0,0 +1,164 @@
1
+ import { $env, $hook, $inject, AlephaError, t } from "alepha";
2
+ import { $logger } from "alepha/logger";
3
+ import type { DrizzleD1Database } from "drizzle-orm/d1";
4
+ import type { PgDatabase } from "drizzle-orm/pg-core";
5
+ import { SqliteModelBuilder } from "../../services/SqliteModelBuilder.ts";
6
+ import { DrizzleKitProvider } from "../DrizzleKitProvider.ts";
7
+ import { DatabaseProvider, type SQLLike } from "./DatabaseProvider.ts";
8
+
9
+ // ---------------------------------------------------------------------------------------------------------------------
10
+
11
+ /**
12
+ * D1Database interface matching Cloudflare's D1 API.
13
+ */
14
+ export interface D1Database {
15
+ prepare(query: string): D1PreparedStatement;
16
+ batch<T = unknown>(statements: D1PreparedStatement[]): Promise<T[]>;
17
+ exec(query: string): Promise<D1ExecResult>;
18
+ dump(): Promise<ArrayBuffer>;
19
+ }
20
+
21
+ export interface D1PreparedStatement {
22
+ bind(...values: unknown[]): D1PreparedStatement;
23
+ first<T = unknown>(colName?: string): Promise<T | null>;
24
+ run(): Promise<D1Result>;
25
+ all<T = unknown>(): Promise<D1Result<T>>;
26
+ raw<T = unknown>(): Promise<T[]>;
27
+ }
28
+
29
+ export interface D1Result<T = unknown> {
30
+ results: T[];
31
+ success: boolean;
32
+ meta: {
33
+ duration: number;
34
+ changes: number;
35
+ last_row_id: number;
36
+ served_by: string;
37
+ internal_stats: unknown;
38
+ };
39
+ }
40
+
41
+ export interface D1ExecResult {
42
+ count: number;
43
+ duration: number;
44
+ }
45
+
46
+ // ---------------------------------------------------------------------------------------------------------------------
47
+
48
+ /**
49
+ * Cloudflare D1 SQLite provider using Drizzle ORM.
50
+ *
51
+ * This provider requires a D1 binding to be set via `cloudflareD1Options` before starting.
52
+ * The binding is typically obtained from the Cloudflare Workers environment.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * // In your Cloudflare Worker
57
+ * alepha.set(cloudflareD1Options, { binding: env.DB });
58
+ * ```
59
+ */
60
+ export class CloudflareD1Provider extends DatabaseProvider {
61
+ protected readonly kit = $inject(DrizzleKitProvider);
62
+ protected readonly log = $logger();
63
+ protected readonly builder = $inject(SqliteModelBuilder);
64
+ protected readonly env = $env(
65
+ t.object({
66
+ DATABASE_URL: t.string({
67
+ description: "Expect to be 'cloudflare-d1://name:id'",
68
+ }),
69
+ }),
70
+ );
71
+
72
+ protected d1?: D1Database;
73
+ protected drizzleDb?: DrizzleD1Database;
74
+
75
+ public get name() {
76
+ return "d1";
77
+ }
78
+
79
+ public override readonly dialect = "sqlite";
80
+
81
+ public override get url(): string {
82
+ return this.env.DATABASE_URL;
83
+ }
84
+
85
+ public override get db(): PgDatabase<any> {
86
+ if (!this.drizzleDb) {
87
+ throw new AlephaError("D1 database not initialized");
88
+ }
89
+
90
+ return this.drizzleDb as unknown as PgDatabase<any>;
91
+ }
92
+
93
+ public override async execute(
94
+ query: SQLLike,
95
+ ): Promise<Array<Record<string, unknown>>> {
96
+ const { rows } = await (this.db as any).run(query);
97
+ return rows;
98
+ }
99
+
100
+ protected readonly onStart = $hook({
101
+ on: "start",
102
+ handler: async () => {
103
+ const [bindingName] = this.env.DATABASE_URL.replace(
104
+ "cloudflare-d1://",
105
+ "",
106
+ ).split(":");
107
+ const cloudflareEnv = this.alepha.store.get("cloudflare.env" as any);
108
+ if (!cloudflareEnv) {
109
+ throw new AlephaError(
110
+ "Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.",
111
+ );
112
+ }
113
+
114
+ const binding = cloudflareEnv[bindingName] as D1Database;
115
+ if (!binding) {
116
+ throw new AlephaError(
117
+ `D1 binding '${bindingName}' not found in Cloudflare Workers environment.`,
118
+ );
119
+ }
120
+
121
+ this.d1 = binding;
122
+
123
+ // Dynamic import to avoid crashes when not on Cloudflare
124
+ const { drizzle } = await import("drizzle-orm/d1");
125
+
126
+ this.drizzleDb = drizzle(this.d1) as DrizzleD1Database;
127
+
128
+ await this.migrateDatabase();
129
+
130
+ this.log.info("Using Cloudflare D1 database");
131
+ },
132
+ });
133
+
134
+ protected async executeMigrations(migrationsFolder: string): Promise<void> {
135
+ // Dynamic import for D1 migrator
136
+ const { migrate } = await import("drizzle-orm/d1/migrator");
137
+ await migrate(this.db as any, { migrationsFolder });
138
+ }
139
+
140
+ /**
141
+ * Override development migration to skip sync (not supported on D1).
142
+ * D1 requires proper migrations to be applied.
143
+ */
144
+ protected override async runDevelopmentMigration(
145
+ migrationsFolder: string,
146
+ ): Promise<void> {
147
+ await this.executeMigrations(migrationsFolder);
148
+ }
149
+
150
+ /**
151
+ * Override test migration to run migrations instead of sync.
152
+ * D1 doesn't support schema synchronization.
153
+ */
154
+ protected override async runTestMigration(): Promise<void> {
155
+ const migrationsFolder = this.getMigrationsFolder();
156
+ try {
157
+ await this.executeMigrations(migrationsFolder);
158
+ } catch {
159
+ this.log.warn(
160
+ "D1 migrations failed in test environment - ensure migrations exist",
161
+ );
162
+ }
163
+ }
164
+ }
@@ -145,7 +145,9 @@ export class NodeSqliteProvider extends DatabaseProvider {
145
145
  on: "start",
146
146
  handler: async () => {
147
147
  const { DatabaseSync } = await import("node:sqlite");
148
- const filepath = this.url.replace("sqlite://", "");
148
+
149
+ const filepath = this.url.replace("sqlite://", "").replace("sqlite:", "");
150
+
149
151
  if (filepath !== ":memory:" && filepath !== "") {
150
152
  const dirname = filepath.split("/").slice(0, -1).join("/");
151
153
  if (dirname) {
@@ -1,4 +1,5 @@
1
1
  import { readFile, unlink, writeFile } from "node:fs/promises";
2
+ import { OPTIONS } from "alepha";
2
3
  import type { Plugin, UserConfig } from "vite";
3
4
  import { boot } from "../helpers/boot.ts";
4
5
  import { fileExists } from "../helpers/fileExists.ts";
@@ -14,6 +15,7 @@ import {
14
15
  generateVercel,
15
16
  prerenderPages,
16
17
  type VercelConfig,
18
+ type WranglerConfig,
17
19
  } from "../tasks/index.ts";
18
20
 
19
21
  export interface ViteAlephaBuildOptions {
@@ -43,7 +45,7 @@ export interface ViteAlephaBuildOptions {
43
45
  *
44
46
  * @default false
45
47
  */
46
- cloudflare?: boolean;
48
+ cloudflare?: boolean | WranglerConfig;
47
49
 
48
50
  /**
49
51
  * If true, the build will be optimized for Docker deployment.
@@ -89,8 +91,9 @@ export async function viteAlephaBuild(
89
91
  let rootConfig: UserConfig = {};
90
92
 
91
93
  return {
92
- name: "alepha-build",
94
+ name: "alepha:build",
93
95
  apply: "build",
96
+ [OPTIONS as any]: options,
94
97
  config(config, ctx) {
95
98
  const buildMode = process.env.ALEPHA_BUILD_MODE as
96
99
  | AlephaBuildMode
@@ -254,8 +257,11 @@ export async function viteAlephaBuild(
254
257
  }
255
258
 
256
259
  if (options.cloudflare) {
260
+ const config =
261
+ typeof options.cloudflare === "boolean" ? {} : options.cloudflare;
257
262
  await generateCloudflare({
258
263
  distDir,
264
+ config,
259
265
  });
260
266
  }
261
267
 
@@ -132,12 +132,16 @@ export async function viteAlephaDev(
132
132
  });
133
133
 
134
134
  server.config.logger.info = (msg: string) => {
135
- runner.app?.log?.info(msg.trim());
135
+ console.log(msg);
136
136
  };
137
137
 
138
138
  server.config.logger.clearScreen = () => {};
139
139
 
140
- await runner.start(server);
140
+ // Return a function - it runs AFTER internal middlewares are set up
141
+ // and after buildStart has been called
142
+ return async () => {
143
+ await runner.start(server);
144
+ };
141
145
  },
142
146
  async closeBundle() {
143
147
  // Cleanup handled by runner
@@ -144,7 +144,7 @@ export async function buildServer(
144
144
 
145
145
  await writeFile(
146
146
  `${opts.distDir}/index.js`,
147
- `${warning}\nimport './server/${entryFile}';${template}`.trim(),
147
+ `${warning}\nimport './server/${entryFile}';\n\n${template}`.trim(),
148
148
  );
149
149
 
150
150
  return { entryFile };
@@ -1,4 +1,4 @@
1
- import { writeFile } from "node:fs/promises";
1
+ import { access, writeFile } from "node:fs/promises";
2
2
  import { basename, join } from "node:path";
3
3
 
4
4
  export interface GenerateCloudflareOptions {
@@ -8,6 +8,15 @@ export interface GenerateCloudflareOptions {
8
8
  * @default "dist"
9
9
  */
10
10
  distDir?: string;
11
+
12
+ /**
13
+ * Additional Wrangler configuration options to merge into wrangler.jsonc.
14
+ */
15
+ config?: WranglerConfig;
16
+ }
17
+
18
+ export interface WranglerConfig {
19
+ [key: string]: any;
11
20
  }
12
21
 
13
22
  const WARNING_COMMENT =
@@ -27,30 +36,47 @@ export async function generateCloudflare(
27
36
  const distDir = opts.distDir ?? "dist";
28
37
  const root = process.cwd();
29
38
  const name = basename(root);
39
+ const hasAssets = await access(join(root, distDir, "public"))
40
+ .then(() => true)
41
+ .catch(() => false);
30
42
 
31
- await writeWranglerConfig(root, distDir, name);
32
- await writeWorkerEntryPoint(root, distDir);
33
- }
34
-
35
- /**
36
- * Write the wrangler.jsonc configuration file for Cloudflare Workers
37
- */
38
- async function writeWranglerConfig(
39
- root: string,
40
- distDir: string,
41
- name: string,
42
- ): Promise<void> {
43
- const wrangler = {
43
+ const wrangler: WranglerConfig = {
44
44
  name,
45
45
  main: "./main.cloudflare.js",
46
46
  compatibility_flags: ["nodejs_compat"],
47
47
  compatibility_date: "2025-11-17",
48
+ ...opts.config,
48
49
  };
49
50
 
51
+ if (hasAssets) {
52
+ wrangler.assets ??= {
53
+ directory: "./public",
54
+ binding: "ASSETS",
55
+ };
56
+ }
57
+
58
+ const url = process.env.DATABASE_URL;
59
+ if (url?.startsWith("cloudflare-d1:")) {
60
+ const [name, id] = url
61
+ .replace("cloudflare-d1://", "")
62
+ .replace("cloudflare-d1:", "")
63
+ .split(":");
64
+ wrangler.d1_databases = wrangler.d1_databases || [];
65
+ wrangler.d1_databases.push({
66
+ binding: name,
67
+ database_name: name,
68
+ database_id: id,
69
+ });
70
+ wrangler.vars ??= {};
71
+ wrangler.vars.DATABASE_URL = `cloudflare-d1://${name}:${id}`;
72
+ }
73
+
50
74
  await writeFile(
51
75
  join(root, distDir, "wrangler.jsonc"),
52
76
  JSON.stringify(wrangler, null, 2),
53
77
  );
78
+
79
+ await writeWorkerEntryPoint(root, distDir);
54
80
  }
55
81
 
56
82
  /**
@@ -64,9 +90,11 @@ async function writeWorkerEntryPoint(
64
90
  import "./index.js";
65
91
 
66
92
  export default {
67
- fetch: async (request) => {
93
+ fetch: async (request, env) => {
68
94
  const ctx = { req: request, res: undefined };
69
95
 
96
+ __alepha.set("cloudflare.env", env);
97
+
70
98
  await __alepha.start();
71
99
  await __alepha.events.emit("web:request", ctx);
72
100
 
@@ -156,6 +156,7 @@ export class AlephaRunner {
156
156
 
157
157
  this.state.app.store.set("alepha.node.server" as any, server.httpServer);
158
158
 
159
+ console.log("");
159
160
  await this.state.app.start();
160
161
  this.state.started = true;
161
162