alepha 0.14.0 → 0.14.1
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/dist/api/audits/index.d.ts +417 -338
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +80 -1
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/jobs/index.d.ts +236 -157
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/notifications/index.d.ts +21 -1
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/parameters/index.d.ts +451 -4
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.d.ts +833 -830
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/cli/index.d.ts +212 -29
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +320 -219
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +206 -9
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +306 -69
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js.map +1 -1
- package/dist/orm/index.d.ts +180 -126
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +486 -512
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/redis/index.js +2 -4
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/redis/index.d.ts +400 -29
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +412 -21
- package/dist/redis/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/security/index.d.ts +28 -28
- package/dist/security/index.d.ts.map +1 -1
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/core/index.d.ts +0 -1
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/health/index.d.ts +17 -17
- package/dist/server/helmet/index.d.ts +4 -1
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/topic/redis/index.js +3 -3
- package/dist/topic/redis/index.js.map +1 -1
- package/dist/vite/index.js +9 -6
- package/dist/vite/index.js.map +1 -1
- package/package.json +3 -3
- package/src/cli/apps/AlephaCli.ts +8 -3
- package/src/cli/apps/AlephaPackageBuilderCli.ts +3 -0
- package/src/cli/atoms/changelogOptions.ts +45 -0
- package/src/cli/commands/ChangelogCommands.ts +187 -317
- package/src/cli/commands/DeployCommands.ts +118 -0
- package/src/cli/commands/DrizzleCommands.ts +28 -8
- package/src/cli/commands/ViteCommands.ts +23 -9
- package/src/cli/defineConfig.ts +15 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/services/AlephaCliUtils.ts +4 -21
- package/src/cli/services/GitMessageParser.ts +77 -0
- package/src/command/helpers/EnvUtils.ts +37 -0
- package/src/command/index.ts +3 -1
- package/src/command/primitives/$command.ts +172 -6
- package/src/command/providers/CliProvider.ts +424 -91
- package/src/core/Alepha.ts +1 -1
- package/src/file/providers/NodeFileSystemProvider.ts +3 -1
- package/src/orm/index.ts +8 -4
- package/src/orm/interfaces/PgQueryWhere.ts +1 -26
- package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
- package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
- package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
- package/src/orm/services/QueryManager.ts +10 -125
- package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
- package/src/redis/index.ts +65 -3
- package/src/redis/providers/BunRedisProvider.ts +304 -0
- package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/NodeRedisProvider.ts +280 -0
- package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/RedisProvider.ts +134 -140
- package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
- package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
- package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
- package/src/server/core/providers/ServerProvider.ts +7 -4
- package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
- package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
- package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
- package/src/vite/tasks/buildServer.ts +1 -0
- package/src/orm/services/PgJsonQueryManager.ts +0 -511
package/src/orm/index.ts
CHANGED
|
@@ -5,13 +5,14 @@ import { $entity } from "./primitives/$entity.ts";
|
|
|
5
5
|
import { $repository } from "./primitives/$repository.ts";
|
|
6
6
|
import { $sequence } from "./primitives/$sequence.ts";
|
|
7
7
|
import { DrizzleKitProvider } from "./providers/DrizzleKitProvider.ts";
|
|
8
|
+
import { BunPostgresProvider } from "./providers/drivers/BunPostgresProvider.ts";
|
|
9
|
+
import { BunSqliteProvider } from "./providers/drivers/BunSqliteProvider.ts";
|
|
8
10
|
import { CloudflareD1Provider } from "./providers/drivers/CloudflareD1Provider.ts";
|
|
9
11
|
import { DatabaseProvider } from "./providers/drivers/DatabaseProvider.ts";
|
|
10
12
|
import { NodePostgresProvider } from "./providers/drivers/NodePostgresProvider.ts";
|
|
11
13
|
import { NodeSqliteProvider } from "./providers/drivers/NodeSqliteProvider.ts";
|
|
12
14
|
import { PglitePostgresProvider } from "./providers/drivers/PglitePostgresProvider.ts";
|
|
13
15
|
import { RepositoryProvider } from "./providers/RepositoryProvider.ts";
|
|
14
|
-
import { PgJsonQueryManager } from "./services/PgJsonQueryManager.ts";
|
|
15
16
|
import { PgRelationManager } from "./services/PgRelationManager.ts";
|
|
16
17
|
import { PostgresModelBuilder } from "./services/PostgresModelBuilder.ts";
|
|
17
18
|
import { QueryManager } from "./services/QueryManager.ts";
|
|
@@ -114,6 +115,8 @@ export * from "./primitives/$repository.ts";
|
|
|
114
115
|
export * from "./primitives/$sequence.ts";
|
|
115
116
|
export * from "./primitives/$transaction.ts";
|
|
116
117
|
export * from "./providers/DrizzleKitProvider.ts";
|
|
118
|
+
export * from "./providers/drivers/BunPostgresProvider.ts";
|
|
119
|
+
export * from "./providers/drivers/BunSqliteProvider.ts";
|
|
117
120
|
export * from "./providers/drivers/CloudflareD1Provider.ts";
|
|
118
121
|
export * from "./providers/drivers/DatabaseProvider.ts";
|
|
119
122
|
export * from "./providers/drivers/NodePostgresProvider.ts";
|
|
@@ -176,6 +179,8 @@ export const AlephaPostgres = $module({
|
|
|
176
179
|
NodePostgresProvider,
|
|
177
180
|
PglitePostgresProvider,
|
|
178
181
|
NodeSqliteProvider,
|
|
182
|
+
BunPostgresProvider,
|
|
183
|
+
BunSqliteProvider,
|
|
179
184
|
CloudflareD1Provider,
|
|
180
185
|
SqliteModelBuilder,
|
|
181
186
|
PostgresModelBuilder,
|
|
@@ -183,7 +188,6 @@ export const AlephaPostgres = $module({
|
|
|
183
188
|
RepositoryProvider,
|
|
184
189
|
Repository,
|
|
185
190
|
PgRelationManager,
|
|
186
|
-
PgJsonQueryManager,
|
|
187
191
|
QueryManager,
|
|
188
192
|
],
|
|
189
193
|
register: (alepha: Alepha) => {
|
|
@@ -225,7 +229,7 @@ export const AlephaPostgres = $module({
|
|
|
225
229
|
alepha.with({
|
|
226
230
|
optional: true,
|
|
227
231
|
provide: DatabaseProvider,
|
|
228
|
-
use: NodePostgresProvider,
|
|
232
|
+
use: alepha.isBun() ? BunPostgresProvider : NodePostgresProvider,
|
|
229
233
|
});
|
|
230
234
|
return;
|
|
231
235
|
}
|
|
@@ -233,7 +237,7 @@ export const AlephaPostgres = $module({
|
|
|
233
237
|
alepha.with({
|
|
234
238
|
optional: true,
|
|
235
239
|
provide: DatabaseProvider,
|
|
236
|
-
use: NodeSqliteProvider,
|
|
240
|
+
use: alepha.isBun() ? BunSqliteProvider : NodeSqliteProvider,
|
|
237
241
|
});
|
|
238
242
|
},
|
|
239
243
|
});
|
|
@@ -20,12 +20,7 @@ export type PgQueryWhereOrSQL<
|
|
|
20
20
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
21
21
|
|
|
22
22
|
type PgQueryWhereOperators<T extends TObject> = {
|
|
23
|
-
[Key in keyof Static<T>]?:
|
|
24
|
-
| FilterOperators<Static<T>[Key]>
|
|
25
|
-
| Static<T>[Key]
|
|
26
|
-
| (Static<T>[Key] extends object
|
|
27
|
-
? NestedJsonbQuery<Static<T>[Key]>
|
|
28
|
-
: never);
|
|
23
|
+
[Key in keyof Static<T>]?: FilterOperators<Static<T>[Key]> | Static<T>[Key];
|
|
29
24
|
};
|
|
30
25
|
|
|
31
26
|
type PgQueryWhereConditions<
|
|
@@ -115,23 +110,3 @@ type PgQueryWhereRelations<
|
|
|
115
110
|
>;
|
|
116
111
|
}
|
|
117
112
|
: {};
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Recursively allow nested queries for JSONB object/array types
|
|
121
|
-
*/
|
|
122
|
-
type NestedJsonbQuery<T> = T extends object
|
|
123
|
-
? T extends Array<infer U>
|
|
124
|
-
? // For arrays, allow querying array element properties
|
|
125
|
-
U extends object
|
|
126
|
-
? {
|
|
127
|
-
[K in keyof U]?: FilterOperators<U[K]> | U[K];
|
|
128
|
-
}
|
|
129
|
-
: FilterOperators<U> | U
|
|
130
|
-
: // For objects, allow nested queries
|
|
131
|
-
{
|
|
132
|
-
[K in keyof T]?:
|
|
133
|
-
| FilterOperators<T[K]>
|
|
134
|
-
| T[K]
|
|
135
|
-
| (T[K] extends object ? NestedJsonbQuery<T[K]> : never);
|
|
136
|
-
}
|
|
137
|
-
: FilterOperators<T> | T;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { $env, $hook, $inject, AlephaError, type Static, t } from "alepha";
|
|
2
|
+
import { $lock } from "alepha/lock";
|
|
3
|
+
import { $logger } from "alepha/logger";
|
|
4
|
+
import type { SQL as BunSQL } from "bun";
|
|
5
|
+
import { sql } from "drizzle-orm";
|
|
6
|
+
import type { BunSQLDatabase } from "drizzle-orm/bun-sql";
|
|
7
|
+
import type { PgDatabase } from "drizzle-orm/pg-core";
|
|
8
|
+
import { DbError } from "../../errors/DbError.ts";
|
|
9
|
+
import { DbMigrationError } from "../../errors/DbMigrationError.ts";
|
|
10
|
+
import { PostgresModelBuilder } from "../../services/PostgresModelBuilder.ts";
|
|
11
|
+
import { DrizzleKitProvider } from "../DrizzleKitProvider.ts";
|
|
12
|
+
import { DatabaseProvider, type SQLLike } from "./DatabaseProvider.ts";
|
|
13
|
+
|
|
14
|
+
declare module "alepha" {
|
|
15
|
+
interface Env extends Partial<Static<typeof envSchema>> {}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const envSchema = t.object({
|
|
19
|
+
/**
|
|
20
|
+
* Main configuration for database connection.
|
|
21
|
+
* Accept a string in the format of a Postgres connection URL.
|
|
22
|
+
* Example: postgres://user:password@localhost:5432/database
|
|
23
|
+
* or
|
|
24
|
+
* Example: postgres://user:password@localhost:5432/database?sslmode=require
|
|
25
|
+
*/
|
|
26
|
+
DATABASE_URL: t.optional(t.text()),
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* In addition to the DATABASE_URL, you can specify the postgres schema name.
|
|
30
|
+
*/
|
|
31
|
+
POSTGRES_SCHEMA: t.optional(t.text()),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Bun PostgreSQL provider using Drizzle ORM with Bun's native SQL client.
|
|
36
|
+
*
|
|
37
|
+
* This provider uses Bun's built-in SQL class for PostgreSQL connections,
|
|
38
|
+
* which provides excellent performance on the Bun runtime.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* // Set DATABASE_URL environment variable
|
|
43
|
+
* // DATABASE_URL=postgres://user:password@localhost:5432/database
|
|
44
|
+
*
|
|
45
|
+
* // Or configure programmatically
|
|
46
|
+
* alepha.with({
|
|
47
|
+
* provide: DatabaseProvider,
|
|
48
|
+
* use: BunPostgresProvider,
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export class BunPostgresProvider extends DatabaseProvider {
|
|
53
|
+
protected readonly log = $logger();
|
|
54
|
+
protected readonly env = $env(envSchema);
|
|
55
|
+
protected readonly kit = $inject(DrizzleKitProvider);
|
|
56
|
+
protected readonly builder = $inject(PostgresModelBuilder);
|
|
57
|
+
|
|
58
|
+
protected client?: BunSQL;
|
|
59
|
+
protected bunDb?: BunSQLDatabase;
|
|
60
|
+
|
|
61
|
+
public readonly dialect = "postgresql";
|
|
62
|
+
|
|
63
|
+
public get name() {
|
|
64
|
+
return "bun-postgres";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* In testing mode, the schema name will be generated and deleted after the test.
|
|
69
|
+
*/
|
|
70
|
+
protected schemaForTesting = this.alepha.isTest()
|
|
71
|
+
? this.env.POSTGRES_SCHEMA?.startsWith("test_")
|
|
72
|
+
? this.env.POSTGRES_SCHEMA
|
|
73
|
+
: this.generateTestSchemaName()
|
|
74
|
+
: undefined;
|
|
75
|
+
|
|
76
|
+
public override get url() {
|
|
77
|
+
if (!this.env.DATABASE_URL) {
|
|
78
|
+
throw new AlephaError("DATABASE_URL is not defined in the environment");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return this.env.DATABASE_URL;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Execute a SQL statement.
|
|
86
|
+
*/
|
|
87
|
+
public override execute(
|
|
88
|
+
statement: SQLLike,
|
|
89
|
+
): Promise<Array<Record<string, unknown>>> {
|
|
90
|
+
try {
|
|
91
|
+
return this.db.execute(statement);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new DbError("Error executing statement", error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get Postgres schema used by this provider.
|
|
99
|
+
*/
|
|
100
|
+
public override get schema(): string {
|
|
101
|
+
if (this.schemaForTesting) {
|
|
102
|
+
return this.schemaForTesting;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (this.env.POSTGRES_SCHEMA) {
|
|
106
|
+
return this.env.POSTGRES_SCHEMA;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return "public";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get the Drizzle Postgres database instance.
|
|
114
|
+
*/
|
|
115
|
+
public override get db(): PgDatabase<any> {
|
|
116
|
+
if (!this.bunDb) {
|
|
117
|
+
throw new AlephaError("Database not initialized");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return this.bunDb as unknown as PgDatabase<any>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected override async executeMigrations(
|
|
124
|
+
migrationsFolder: string,
|
|
125
|
+
): Promise<void> {
|
|
126
|
+
const { migrate } = await import("drizzle-orm/bun-sql/migrator");
|
|
127
|
+
await migrate(this.bunDb!, { migrationsFolder });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
protected readonly onStart = $hook({
|
|
133
|
+
on: "start",
|
|
134
|
+
handler: async () => {
|
|
135
|
+
await this.connect();
|
|
136
|
+
|
|
137
|
+
// never migrate in serverless mode (vercel, netlify, ...)
|
|
138
|
+
if (!this.alepha.isServerless()) {
|
|
139
|
+
try {
|
|
140
|
+
await this.migrate.run();
|
|
141
|
+
} catch (error) {
|
|
142
|
+
throw new DbMigrationError(error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
protected readonly onStop = $hook({
|
|
149
|
+
on: "stop",
|
|
150
|
+
handler: async () => {
|
|
151
|
+
// cleanup test schema
|
|
152
|
+
if (
|
|
153
|
+
this.alepha.isTest() &&
|
|
154
|
+
this.schemaForTesting &&
|
|
155
|
+
this.schemaForTesting.startsWith("test_")
|
|
156
|
+
) {
|
|
157
|
+
// Additional validation: schema name must only contain safe characters
|
|
158
|
+
if (!/^test_[a-z0-9_]+$/i.test(this.schemaForTesting)) {
|
|
159
|
+
throw new AlephaError(
|
|
160
|
+
`Invalid test schema name: ${this.schemaForTesting}. Must match pattern: test_[a-z0-9_]+`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this.log.warn(`Deleting test schema '${this.schemaForTesting}' ...`);
|
|
165
|
+
await this.execute(
|
|
166
|
+
sql`DROP SCHEMA IF EXISTS ${sql.raw(this.schemaForTesting)} CASCADE`,
|
|
167
|
+
);
|
|
168
|
+
this.log.info(`Test schema '${this.schemaForTesting}' deleted`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// close the connection
|
|
172
|
+
await this.close();
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
public async connect(): Promise<void> {
|
|
177
|
+
this.log.debug("Connect ..");
|
|
178
|
+
|
|
179
|
+
// Check if we're running in Bun
|
|
180
|
+
if (typeof Bun === "undefined") {
|
|
181
|
+
throw new AlephaError(
|
|
182
|
+
"BunPostgresProvider requires the Bun runtime. Use NodePostgresProvider for Node.js.",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const { drizzle } = await import("drizzle-orm/bun-sql");
|
|
187
|
+
const { SQL } = await import("bun");
|
|
188
|
+
|
|
189
|
+
// Create Bun SQL client
|
|
190
|
+
this.client = new SQL(this.url);
|
|
191
|
+
|
|
192
|
+
// Test connection
|
|
193
|
+
await this.client.unsafe("SELECT 1");
|
|
194
|
+
|
|
195
|
+
this.bunDb = drizzle({
|
|
196
|
+
client: this.client,
|
|
197
|
+
logger: {
|
|
198
|
+
logQuery: (query: string, params: unknown[]) => {
|
|
199
|
+
this.log.trace(query, { params });
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
this.log.info("Connection OK");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public async close(): Promise<void> {
|
|
208
|
+
if (this.client) {
|
|
209
|
+
this.log.debug("Close...");
|
|
210
|
+
|
|
211
|
+
await this.client.close();
|
|
212
|
+
|
|
213
|
+
this.client = undefined;
|
|
214
|
+
this.bunDb = undefined;
|
|
215
|
+
|
|
216
|
+
this.log.info("Connection closed");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
protected migrate = $lock({
|
|
221
|
+
handler: async () => {
|
|
222
|
+
await this.migrateDatabase();
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import {
|
|
4
|
+
$atom,
|
|
5
|
+
$env,
|
|
6
|
+
$hook,
|
|
7
|
+
$inject,
|
|
8
|
+
$use,
|
|
9
|
+
AlephaError,
|
|
10
|
+
type Static,
|
|
11
|
+
t,
|
|
12
|
+
} from "alepha";
|
|
13
|
+
import { $logger } from "alepha/logger";
|
|
14
|
+
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
|
|
15
|
+
import type { PgDatabase } from "drizzle-orm/pg-core";
|
|
16
|
+
import { SqliteModelBuilder } from "../../services/SqliteModelBuilder.ts";
|
|
17
|
+
import { DrizzleKitProvider } from "../DrizzleKitProvider.ts";
|
|
18
|
+
import { DatabaseProvider, type SQLLike } from "./DatabaseProvider.ts";
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
const envSchema = t.object({
|
|
23
|
+
DATABASE_URL: t.optional(t.text()),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Configuration options for the Bun SQLite database provider.
|
|
28
|
+
*/
|
|
29
|
+
export const bunSqliteOptions = $atom({
|
|
30
|
+
name: "alepha.postgres.bun-sqlite.options",
|
|
31
|
+
schema: t.object({
|
|
32
|
+
path: t.optional(
|
|
33
|
+
t.string({
|
|
34
|
+
description:
|
|
35
|
+
"Filepath or :memory:. If empty, provider will use DATABASE_URL from env.",
|
|
36
|
+
}),
|
|
37
|
+
),
|
|
38
|
+
}),
|
|
39
|
+
default: {},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export type BunSqliteProviderOptions = Static<typeof bunSqliteOptions.schema>;
|
|
43
|
+
|
|
44
|
+
declare module "alepha" {
|
|
45
|
+
interface State {
|
|
46
|
+
[bunSqliteOptions.key]: BunSqliteProviderOptions;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Bun SQLite provider using Drizzle ORM with Bun's native SQLite client.
|
|
54
|
+
*
|
|
55
|
+
* This provider uses Bun's built-in `bun:sqlite` for SQLite connections,
|
|
56
|
+
* which provides excellent performance on the Bun runtime.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* // Set DATABASE_URL environment variable
|
|
61
|
+
* // DATABASE_URL=sqlite://./my-database.db
|
|
62
|
+
*
|
|
63
|
+
* // Or configure programmatically
|
|
64
|
+
* alepha.with({
|
|
65
|
+
* provide: DatabaseProvider,
|
|
66
|
+
* use: BunSqliteProvider,
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* // Or use options atom
|
|
70
|
+
* alepha.store.mut(bunSqliteOptions, (old) => ({
|
|
71
|
+
* ...old,
|
|
72
|
+
* path: ":memory:",
|
|
73
|
+
* }));
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export class BunSqliteProvider extends DatabaseProvider {
|
|
77
|
+
protected readonly kit = $inject(DrizzleKitProvider);
|
|
78
|
+
protected readonly log = $logger();
|
|
79
|
+
protected readonly env = $env(envSchema);
|
|
80
|
+
protected readonly builder = $inject(SqliteModelBuilder);
|
|
81
|
+
protected readonly options = $use(bunSqliteOptions);
|
|
82
|
+
|
|
83
|
+
protected sqlite?: Database;
|
|
84
|
+
protected bunDb?: BunSQLiteDatabase;
|
|
85
|
+
|
|
86
|
+
public get name() {
|
|
87
|
+
return "bun-sqlite";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public override readonly dialect = "sqlite";
|
|
91
|
+
|
|
92
|
+
public override get url(): string {
|
|
93
|
+
const path = this.options.path ?? this.env.DATABASE_URL;
|
|
94
|
+
if (path) {
|
|
95
|
+
if (path.startsWith("postgres://")) {
|
|
96
|
+
throw new AlephaError(
|
|
97
|
+
"Postgres URL is not supported for SQLite provider.",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return path;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (this.alepha.isTest() || this.alepha.isServerless()) {
|
|
104
|
+
return ":memory:";
|
|
105
|
+
} else {
|
|
106
|
+
return "node_modules/.alepha/bun-sqlite.db";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public override get db(): PgDatabase<any> {
|
|
111
|
+
if (!this.bunDb) {
|
|
112
|
+
throw new AlephaError("Database not initialized");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return this.bunDb as unknown as PgDatabase<any>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public override async execute(
|
|
119
|
+
query: SQLLike,
|
|
120
|
+
): Promise<Array<Record<string, unknown>>> {
|
|
121
|
+
return (this.bunDb as BunSQLiteDatabase).all(query);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected readonly onStart = $hook({
|
|
125
|
+
on: "start",
|
|
126
|
+
handler: async () => {
|
|
127
|
+
// Check if we're running in Bun
|
|
128
|
+
if (typeof Bun === "undefined") {
|
|
129
|
+
throw new AlephaError(
|
|
130
|
+
"BunSqliteProvider requires the Bun runtime. Use NodeSqliteProvider for Node.js.",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const { Database } = await import("bun:sqlite");
|
|
135
|
+
const { drizzle } = await import("drizzle-orm/bun-sqlite");
|
|
136
|
+
|
|
137
|
+
const filepath = this.url.replace("sqlite://", "").replace("sqlite:", "");
|
|
138
|
+
|
|
139
|
+
if (filepath !== ":memory:" && filepath !== "") {
|
|
140
|
+
const dirname = filepath.split("/").slice(0, -1).join("/");
|
|
141
|
+
if (dirname) {
|
|
142
|
+
await mkdir(dirname, { recursive: true }).catch(() => null);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.sqlite = new Database(filepath);
|
|
147
|
+
|
|
148
|
+
this.bunDb = drizzle({
|
|
149
|
+
client: this.sqlite,
|
|
150
|
+
logger: {
|
|
151
|
+
logQuery: (query: string, params: unknown[]) => {
|
|
152
|
+
this.log.trace(query, { params });
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await this.migrateDatabase();
|
|
158
|
+
|
|
159
|
+
this.log.info(`Using Bun SQLite database at ${filepath}`);
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
protected readonly onStop = $hook({
|
|
164
|
+
on: "stop",
|
|
165
|
+
handler: async () => {
|
|
166
|
+
if (this.sqlite) {
|
|
167
|
+
this.log.debug("Closing Bun SQLite connection...");
|
|
168
|
+
this.sqlite.close();
|
|
169
|
+
this.sqlite = undefined;
|
|
170
|
+
this.bunDb = undefined;
|
|
171
|
+
this.log.info("Bun SQLite connection closed");
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
protected async executeMigrations(migrationsFolder: string): Promise<void> {
|
|
177
|
+
const { migrate } = await import("drizzle-orm/bun-sqlite/migrator");
|
|
178
|
+
await migrate(this.bunDb!, { migrationsFolder });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -176,4 +176,29 @@ export abstract class DatabaseProvider {
|
|
|
176
176
|
* MUST be implemented by each provider
|
|
177
177
|
*/
|
|
178
178
|
protected abstract executeMigrations(migrationsFolder: string): Promise<void>;
|
|
179
|
+
|
|
180
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* For testing purposes, generate a unique schema name.
|
|
184
|
+
* The schema name will be generated based on the current date and time.
|
|
185
|
+
* It will be in the format of `test_YYYYMMDD_HHMMSS_randomSuffix`.
|
|
186
|
+
*/
|
|
187
|
+
protected generateTestSchemaName(): string {
|
|
188
|
+
const pad = (n: number) => n.toString().padStart(2, "0");
|
|
189
|
+
|
|
190
|
+
const now = new Date();
|
|
191
|
+
const year = now.getUTCFullYear();
|
|
192
|
+
const month = pad(now.getUTCMonth() + 1);
|
|
193
|
+
const day = pad(now.getUTCDate());
|
|
194
|
+
const hours = pad(now.getUTCHours());
|
|
195
|
+
const minutes = pad(now.getUTCMinutes());
|
|
196
|
+
const seconds = pad(now.getUTCSeconds());
|
|
197
|
+
|
|
198
|
+
const timestamp = `${year}${month}${day}_${hours}${minutes}${seconds}`;
|
|
199
|
+
|
|
200
|
+
const randomSuffix = Math.random().toString(36).slice(2, 6); // 4 alphanumeric chars
|
|
201
|
+
|
|
202
|
+
return `test_${timestamp}_${randomSuffix}`;
|
|
203
|
+
}
|
|
179
204
|
}
|
|
@@ -231,29 +231,4 @@ export class NodePostgresProvider extends DatabaseProvider {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
|
-
|
|
235
|
-
// -------------------------------------------------------------------------------------------------------------------
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* For testing purposes, generate a unique schema name.
|
|
239
|
-
* The schema name will be generated based on the current date and time.
|
|
240
|
-
* It will be in the format of `test_YYYYMMDD_HHMMSS_randomSuffix`.
|
|
241
|
-
*/
|
|
242
|
-
protected generateTestSchemaName(): string {
|
|
243
|
-
const pad = (n: number) => n.toString().padStart(2, "0");
|
|
244
|
-
|
|
245
|
-
const now = new Date();
|
|
246
|
-
const year = now.getUTCFullYear();
|
|
247
|
-
const month = pad(now.getUTCMonth() + 1);
|
|
248
|
-
const day = pad(now.getUTCDate());
|
|
249
|
-
const hours = pad(now.getUTCHours());
|
|
250
|
-
const minutes = pad(now.getUTCMinutes());
|
|
251
|
-
const seconds = pad(now.getUTCSeconds());
|
|
252
|
-
|
|
253
|
-
const timestamp = `${year}${month}${day}_${hours}${minutes}${seconds}`;
|
|
254
|
-
|
|
255
|
-
const randomSuffix = Math.random().toString(36).slice(2, 6); // 4 alphanumeric chars
|
|
256
|
-
|
|
257
|
-
return `test_${timestamp}_${randomSuffix}`;
|
|
258
|
-
}
|
|
259
234
|
}
|