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.
Files changed (94) hide show
  1. package/dist/api/audits/index.d.ts +417 -338
  2. package/dist/api/audits/index.d.ts.map +1 -1
  3. package/dist/api/files/index.d.ts +80 -1
  4. package/dist/api/files/index.d.ts.map +1 -1
  5. package/dist/api/jobs/index.d.ts +236 -157
  6. package/dist/api/jobs/index.d.ts.map +1 -1
  7. package/dist/api/notifications/index.d.ts +21 -1
  8. package/dist/api/notifications/index.d.ts.map +1 -1
  9. package/dist/api/parameters/index.d.ts +451 -4
  10. package/dist/api/parameters/index.d.ts.map +1 -1
  11. package/dist/api/users/index.d.ts +833 -830
  12. package/dist/api/users/index.d.ts.map +1 -1
  13. package/dist/cli/index.d.ts +212 -29
  14. package/dist/cli/index.d.ts.map +1 -1
  15. package/dist/cli/index.js +320 -219
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/command/index.d.ts +206 -9
  18. package/dist/command/index.d.ts.map +1 -1
  19. package/dist/command/index.js +306 -69
  20. package/dist/command/index.js.map +1 -1
  21. package/dist/core/index.browser.js.map +1 -1
  22. package/dist/core/index.d.ts +1 -1
  23. package/dist/core/index.js.map +1 -1
  24. package/dist/core/index.native.js.map +1 -1
  25. package/dist/file/index.d.ts.map +1 -1
  26. package/dist/file/index.js.map +1 -1
  27. package/dist/orm/index.d.ts +180 -126
  28. package/dist/orm/index.d.ts.map +1 -1
  29. package/dist/orm/index.js +486 -512
  30. package/dist/orm/index.js.map +1 -1
  31. package/dist/queue/redis/index.js +2 -4
  32. package/dist/queue/redis/index.js.map +1 -1
  33. package/dist/redis/index.d.ts +400 -29
  34. package/dist/redis/index.d.ts.map +1 -1
  35. package/dist/redis/index.js +412 -21
  36. package/dist/redis/index.js.map +1 -1
  37. package/dist/scheduler/index.d.ts +6 -6
  38. package/dist/security/index.d.ts +28 -28
  39. package/dist/security/index.d.ts.map +1 -1
  40. package/dist/server/auth/index.d.ts +155 -155
  41. package/dist/server/core/index.d.ts +0 -1
  42. package/dist/server/core/index.d.ts.map +1 -1
  43. package/dist/server/core/index.js.map +1 -1
  44. package/dist/server/health/index.d.ts +17 -17
  45. package/dist/server/helmet/index.d.ts +4 -1
  46. package/dist/server/helmet/index.d.ts.map +1 -1
  47. package/dist/server/multipart/index.d.ts.map +1 -1
  48. package/dist/server/multipart/index.js.map +1 -1
  49. package/dist/server/proxy/index.js.map +1 -1
  50. package/dist/topic/redis/index.js +3 -3
  51. package/dist/topic/redis/index.js.map +1 -1
  52. package/dist/vite/index.js +9 -6
  53. package/dist/vite/index.js.map +1 -1
  54. package/package.json +3 -3
  55. package/src/cli/apps/AlephaCli.ts +8 -3
  56. package/src/cli/apps/AlephaPackageBuilderCli.ts +3 -0
  57. package/src/cli/atoms/changelogOptions.ts +45 -0
  58. package/src/cli/commands/ChangelogCommands.ts +187 -317
  59. package/src/cli/commands/DeployCommands.ts +118 -0
  60. package/src/cli/commands/DrizzleCommands.ts +28 -8
  61. package/src/cli/commands/ViteCommands.ts +23 -9
  62. package/src/cli/defineConfig.ts +15 -0
  63. package/src/cli/index.ts +3 -0
  64. package/src/cli/services/AlephaCliUtils.ts +4 -21
  65. package/src/cli/services/GitMessageParser.ts +77 -0
  66. package/src/command/helpers/EnvUtils.ts +37 -0
  67. package/src/command/index.ts +3 -1
  68. package/src/command/primitives/$command.ts +172 -6
  69. package/src/command/providers/CliProvider.ts +424 -91
  70. package/src/core/Alepha.ts +1 -1
  71. package/src/file/providers/NodeFileSystemProvider.ts +3 -1
  72. package/src/orm/index.ts +8 -4
  73. package/src/orm/interfaces/PgQueryWhere.ts +1 -26
  74. package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
  75. package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
  76. package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
  77. package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
  78. package/src/orm/services/QueryManager.ts +10 -125
  79. package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
  80. package/src/redis/index.ts +65 -3
  81. package/src/redis/providers/BunRedisProvider.ts +304 -0
  82. package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
  83. package/src/redis/providers/NodeRedisProvider.ts +280 -0
  84. package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
  85. package/src/redis/providers/RedisProvider.ts +134 -140
  86. package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
  87. package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
  88. package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
  89. package/src/server/core/providers/ServerProvider.ts +7 -4
  90. package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
  91. package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
  92. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  93. package/src/vite/tasks/buildServer.ts +1 -0
  94. 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
  }