alepha 0.14.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/api/audits/index.d.ts +80 -1
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js.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/files/index.js.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/jobs/index.js.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/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +252 -249
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +4 -0
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +128 -128
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cli/index.d.ts +304 -115
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +650 -531
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +210 -13
- 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.d.ts.map +1 -1
- package/dist/core/index.js +7 -6
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +7 -6
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +26 -5
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.d.ts +294 -215
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +522 -523
- 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/retry/index.js.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- 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/helmet/index.d.ts +4 -1
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/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/server/rate-limit/index.js.map +1 -1
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/security/index.js.map +1 -1
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/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/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +7 -7
- package/dist/websocket/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api/users/index.ts +4 -0
- package/src/cli/apps/AlephaCli.ts +36 -14
- package/src/cli/apps/AlephaPackageBuilderCli.ts +5 -1
- package/src/cli/assets/appRouterTs.ts +1 -1
- package/src/cli/atoms/changelogOptions.ts +45 -0
- package/src/cli/commands/{ViteCommands.ts → build.ts} +4 -93
- package/src/cli/commands/changelog.ts +244 -0
- package/src/cli/commands/clean.ts +14 -0
- package/src/cli/commands/{DrizzleCommands.ts → db.ts} +37 -124
- package/src/cli/commands/deploy.ts +118 -0
- package/src/cli/commands/dev.ts +57 -0
- package/src/cli/commands/format.ts +17 -0
- package/src/cli/commands/{CoreCommands.ts → init.ts} +2 -40
- package/src/cli/commands/lint.ts +17 -0
- package/src/cli/commands/root.ts +32 -0
- package/src/cli/commands/run.ts +24 -0
- package/src/cli/commands/test.ts +42 -0
- package/src/cli/commands/typecheck.ts +19 -0
- package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
- package/src/cli/defineConfig.ts +24 -0
- package/src/cli/index.ts +17 -5
- 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 +8 -5
- package/src/file/providers/NodeFileSystemProvider.ts +3 -1
- package/src/orm/index.browser.ts +1 -1
- package/src/orm/index.ts +18 -10
- package/src/orm/interfaces/PgQueryWhere.ts +1 -26
- package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
- 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/cli/commands/BiomeCommands.ts +0 -29
- package/src/cli/commands/ChangelogCommands.ts +0 -389
- package/src/orm/services/PgJsonQueryManager.ts +0 -511
package/dist/orm/index.js
CHANGED
|
@@ -3,15 +3,15 @@ import { $atom, $context, $env, $hook, $inject, $module, $use, Alepha, AlephaErr
|
|
|
3
3
|
import { AlephaDateTime, DateTimeProvider } from "alepha/datetime";
|
|
4
4
|
import * as drizzle from "drizzle-orm";
|
|
5
5
|
import { and, arrayContained, arrayContains, arrayOverlaps, asc, between, desc, eq, getTableName, gt, gte, ilike, inArray, isNotNull, isNull, isSQLWrapper, like, lt, lte, ne, not, notBetween, notIlike, notInArray, notLike, or, sql, sql as sql$1 } from "drizzle-orm";
|
|
6
|
-
import * as pg$
|
|
6
|
+
import * as pg$2 from "drizzle-orm/pg-core";
|
|
7
7
|
import { alias, check, customType, foreignKey, index, pgEnum, pgSchema, pgTable, unique, uniqueIndex } from "drizzle-orm/pg-core";
|
|
8
8
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
9
9
|
import { $logger } from "alepha/logger";
|
|
10
10
|
import { isSQLWrapper as isSQLWrapper$1 } from "drizzle-orm/sql/sql";
|
|
11
|
+
import { $lock } from "alepha/lock";
|
|
11
12
|
import { randomUUID } from "node:crypto";
|
|
12
|
-
import * as pg$
|
|
13
|
+
import * as pg$1 from "drizzle-orm/sqlite-core";
|
|
13
14
|
import { check as check$1, foreignKey as foreignKey$1, index as index$1, sqliteTable, unique as unique$1, uniqueIndex as uniqueIndex$1 } from "drizzle-orm/sqlite-core";
|
|
14
|
-
import { $lock } from "alepha/lock";
|
|
15
15
|
import { drizzle as drizzle$1 } from "drizzle-orm/postgres-js";
|
|
16
16
|
import { migrate } from "drizzle-orm/postgres-js/migrator";
|
|
17
17
|
import postgres from "postgres";
|
|
@@ -289,6 +289,16 @@ var DatabaseProvider = class {
|
|
|
289
289
|
throw new DbError(`Failed to synchronize ${this.dialect} database schema`, error);
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* For testing purposes, generate a unique schema name.
|
|
294
|
+
* The schema name will be generated based on the current date and time.
|
|
295
|
+
* It will be in the format of `test_YYYYMMDD_HHMMSS_randomSuffix`.
|
|
296
|
+
*/
|
|
297
|
+
generateTestSchemaName() {
|
|
298
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
299
|
+
const now = /* @__PURE__ */ new Date();
|
|
300
|
+
return `test_${`${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}_${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(now.getUTCSeconds())}`}_${Math.random().toString(36).slice(2, 6)}`;
|
|
301
|
+
}
|
|
292
302
|
};
|
|
293
303
|
|
|
294
304
|
//#endregion
|
|
@@ -353,233 +363,9 @@ var PgRelationManager = class {
|
|
|
353
363
|
}
|
|
354
364
|
};
|
|
355
365
|
|
|
356
|
-
//#endregion
|
|
357
|
-
//#region ../../src/orm/services/PgJsonQueryManager.ts
|
|
358
|
-
/**
|
|
359
|
-
* Manages JSONB query generation for nested object and array queries in PostgreSQL.
|
|
360
|
-
* This class handles complex nested queries using PostgreSQL's JSONB operators.
|
|
361
|
-
*/
|
|
362
|
-
var PgJsonQueryManager = class {
|
|
363
|
-
/**
|
|
364
|
-
* Check if a query contains nested JSONB queries.
|
|
365
|
-
* A nested query is when the value is an object with operator keys.
|
|
366
|
-
*/
|
|
367
|
-
hasNestedQuery(where) {
|
|
368
|
-
for (const [key, value] of Object.entries(where)) {
|
|
369
|
-
if (key === "and" || key === "or" || key === "not") continue;
|
|
370
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
371
|
-
const keys = Object.keys(value);
|
|
372
|
-
if (!keys.some((k) => [
|
|
373
|
-
"eq",
|
|
374
|
-
"ne",
|
|
375
|
-
"gt",
|
|
376
|
-
"gte",
|
|
377
|
-
"lt",
|
|
378
|
-
"lte",
|
|
379
|
-
"like",
|
|
380
|
-
"ilike",
|
|
381
|
-
"isNull",
|
|
382
|
-
"isNotNull",
|
|
383
|
-
"inArray",
|
|
384
|
-
"notInArray"
|
|
385
|
-
].includes(k)) && keys.length > 0) return true;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
return false;
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Build a JSONB query condition for nested object queries.
|
|
392
|
-
* Supports deep nesting like: { profile: { contact: { email: { eq: "test@example.com" } } } }
|
|
393
|
-
*
|
|
394
|
-
* @param column The JSONB column
|
|
395
|
-
* @param path The path to the nested property (e.g., ['profile', 'contact', 'email'])
|
|
396
|
-
* @param operator The filter operator (e.g., { eq: "test@example.com" })
|
|
397
|
-
* @param dialect Database dialect (postgresql or sqlite)
|
|
398
|
-
* @param columnSchema Optional schema of the JSON column for type inference
|
|
399
|
-
* @returns SQL condition
|
|
400
|
-
*/
|
|
401
|
-
buildJsonbCondition(column, path, operator, dialect, columnSchema) {
|
|
402
|
-
if (path.length === 0) return;
|
|
403
|
-
const isArrayOperator = operator.arrayContains !== void 0 || operator.arrayContained !== void 0 || operator.arrayOverlaps !== void 0;
|
|
404
|
-
let jsonValue;
|
|
405
|
-
if (dialect === "sqlite") jsonValue = sql$1`json_extract(${column}, ${`$.${path.join(".")}`})`;
|
|
406
|
-
else {
|
|
407
|
-
let jsonPath = sql$1`${column}`;
|
|
408
|
-
for (let i = 0; i < path.length - 1; i++) jsonPath = sql$1`${jsonPath}->${path[i]}`;
|
|
409
|
-
const lastPath = path[path.length - 1];
|
|
410
|
-
if (isArrayOperator) jsonValue = sql$1`${jsonPath}->${lastPath}`;
|
|
411
|
-
else jsonValue = sql$1`${jsonPath}->>${lastPath}`;
|
|
412
|
-
}
|
|
413
|
-
const fieldType = columnSchema ? this.getFieldType(columnSchema, path) : void 0;
|
|
414
|
-
return this.applyOperatorToJsonValue(jsonValue, operator, dialect, fieldType);
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Build JSONB array query conditions.
|
|
418
|
-
* Supports queries like: { addresses: { city: { eq: "Wonderland" } } }
|
|
419
|
-
* which translates to: EXISTS (SELECT 1 FROM jsonb_array_elements(addresses) elem WHERE elem->>'city' = 'Wonderland')
|
|
420
|
-
*
|
|
421
|
-
* @param dialect Database dialect (postgresql or sqlite)
|
|
422
|
-
* Note: SQLite array queries are not yet supported
|
|
423
|
-
*/
|
|
424
|
-
buildJsonbArrayCondition(column, path, arrayPath, operator, dialect) {
|
|
425
|
-
if (dialect === "sqlite") throw new Error("Array queries in JSON columns are not yet supported for SQLite. Please use PostgreSQL for complex JSON array queries, or restructure your data.");
|
|
426
|
-
if (path.length === 0) return;
|
|
427
|
-
let jsonPath = sql$1`${column}`;
|
|
428
|
-
if (arrayPath) jsonPath = sql$1`${jsonPath}->${arrayPath}`;
|
|
429
|
-
const elemCondition = sql$1`elem->>${path[0]}`;
|
|
430
|
-
const condition = this.applyOperatorToJsonValue(elemCondition, operator, dialect);
|
|
431
|
-
if (!condition) return;
|
|
432
|
-
return sql$1`EXISTS (SELECT 1 FROM jsonb_array_elements(${jsonPath}) AS elem WHERE ${condition})`;
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Apply a filter operator to a JSONB value.
|
|
436
|
-
* @param dialect Database dialect for appropriate casting syntax
|
|
437
|
-
* @param fieldType Optional field type from schema for smart casting
|
|
438
|
-
*/
|
|
439
|
-
applyOperatorToJsonValue(jsonValue, operator, dialect, fieldType) {
|
|
440
|
-
const castForNumeric = (value) => {
|
|
441
|
-
if (dialect === "sqlite") {
|
|
442
|
-
if (fieldType === "integer" || fieldType === "int") return sql$1`CAST(${value} AS INTEGER)`;
|
|
443
|
-
return sql$1`CAST(${value} AS REAL)`;
|
|
444
|
-
}
|
|
445
|
-
return sql$1`(${value})::numeric`;
|
|
446
|
-
};
|
|
447
|
-
if (typeof operator !== "object") return sql$1`${jsonValue} = ${operator}`;
|
|
448
|
-
const conditions = [];
|
|
449
|
-
if (operator.eq !== void 0) conditions.push(sql$1`${jsonValue} = ${operator.eq}`);
|
|
450
|
-
if (operator.ne !== void 0) conditions.push(sql$1`${jsonValue} != ${operator.ne}`);
|
|
451
|
-
if (operator.gt !== void 0) conditions.push(sql$1`${castForNumeric(jsonValue)} > ${operator.gt}`);
|
|
452
|
-
if (operator.gte !== void 0) conditions.push(sql$1`${castForNumeric(jsonValue)} >= ${operator.gte}`);
|
|
453
|
-
if (operator.lt !== void 0) conditions.push(sql$1`${castForNumeric(jsonValue)} < ${operator.lt}`);
|
|
454
|
-
if (operator.lte !== void 0) conditions.push(sql$1`${castForNumeric(jsonValue)} <= ${operator.lte}`);
|
|
455
|
-
if (operator.like !== void 0) conditions.push(sql$1`${jsonValue} LIKE ${operator.like}`);
|
|
456
|
-
if (operator.ilike !== void 0) if (dialect === "sqlite") conditions.push(sql$1`${jsonValue} LIKE ${operator.ilike}`);
|
|
457
|
-
else conditions.push(sql$1`${jsonValue} ILIKE ${operator.ilike}`);
|
|
458
|
-
if (operator.notLike !== void 0) conditions.push(sql$1`${jsonValue} NOT LIKE ${operator.notLike}`);
|
|
459
|
-
if (operator.notIlike !== void 0) if (dialect === "sqlite") conditions.push(sql$1`${jsonValue} NOT LIKE ${operator.notIlike}`);
|
|
460
|
-
else conditions.push(sql$1`${jsonValue} NOT ILIKE ${operator.notIlike}`);
|
|
461
|
-
if (operator.isNull !== void 0) conditions.push(sql$1`${jsonValue} IS NULL`);
|
|
462
|
-
if (operator.isNotNull !== void 0) conditions.push(sql$1`${jsonValue} IS NOT NULL`);
|
|
463
|
-
if (operator.inArray !== void 0 && Array.isArray(operator.inArray)) conditions.push(sql$1`${jsonValue} IN (${sql$1.join(operator.inArray.map((v) => sql$1`${v}`), sql$1`, `)})`);
|
|
464
|
-
if (operator.notInArray !== void 0 && Array.isArray(operator.notInArray)) conditions.push(sql$1`${jsonValue} NOT IN (${sql$1.join(operator.notInArray.map((v) => sql$1`${v}`), sql$1`, `)})`);
|
|
465
|
-
if (operator.arrayContains !== void 0) {
|
|
466
|
-
if (dialect === "postgresql") {
|
|
467
|
-
const jsonArray = JSON.stringify(Array.isArray(operator.arrayContains) ? operator.arrayContains : [operator.arrayContains]);
|
|
468
|
-
conditions.push(sql$1`${jsonValue} @> ${jsonArray}::jsonb`);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
if (operator.arrayContained !== void 0) {
|
|
472
|
-
if (dialect === "postgresql") {
|
|
473
|
-
const jsonArray = JSON.stringify(Array.isArray(operator.arrayContained) ? operator.arrayContained : [operator.arrayContained]);
|
|
474
|
-
conditions.push(sql$1`${jsonValue} <@ ${jsonArray}::jsonb`);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
if (operator.arrayOverlaps !== void 0) {
|
|
478
|
-
if (dialect === "postgresql") {
|
|
479
|
-
const overlapConditions = (Array.isArray(operator.arrayOverlaps) ? operator.arrayOverlaps : [operator.arrayOverlaps]).map((val) => {
|
|
480
|
-
return sql$1`${jsonValue} @> ${JSON.stringify(val)}::jsonb`;
|
|
481
|
-
});
|
|
482
|
-
if (overlapConditions.length > 0) conditions.push(sql$1`(${sql$1.join(overlapConditions, sql$1` OR `)})`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (conditions.length === 0) return;
|
|
486
|
-
if (conditions.length === 1) return conditions[0];
|
|
487
|
-
return sql$1.join(conditions, sql$1` AND `);
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Parse a nested query object and extract the path and operator.
|
|
491
|
-
* For example: { profile: { contact: { email: { eq: "test@example.com" } } } }
|
|
492
|
-
* Returns: { path: ['profile', 'contact', 'email'], operator: { eq: "test@example.com" } }
|
|
493
|
-
*/
|
|
494
|
-
parseNestedQuery(nestedQuery, currentPath = []) {
|
|
495
|
-
const results = [];
|
|
496
|
-
for (const [key, value] of Object.entries(nestedQuery)) if (value && typeof value === "object" && !Array.isArray(value)) if (Object.keys(value).some((k) => [
|
|
497
|
-
"eq",
|
|
498
|
-
"ne",
|
|
499
|
-
"gt",
|
|
500
|
-
"gte",
|
|
501
|
-
"lt",
|
|
502
|
-
"lte",
|
|
503
|
-
"like",
|
|
504
|
-
"ilike",
|
|
505
|
-
"notLike",
|
|
506
|
-
"notIlike",
|
|
507
|
-
"isNull",
|
|
508
|
-
"isNotNull",
|
|
509
|
-
"inArray",
|
|
510
|
-
"notInArray",
|
|
511
|
-
"arrayContains",
|
|
512
|
-
"arrayContained",
|
|
513
|
-
"arrayOverlaps"
|
|
514
|
-
].includes(k))) results.push({
|
|
515
|
-
path: [...currentPath, key],
|
|
516
|
-
operator: value
|
|
517
|
-
});
|
|
518
|
-
else {
|
|
519
|
-
const nestedResults = this.parseNestedQuery(value, [...currentPath, key]);
|
|
520
|
-
results.push(...nestedResults);
|
|
521
|
-
}
|
|
522
|
-
return results;
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Determine if a property is a JSONB column based on the schema.
|
|
526
|
-
* A column is JSONB if it's defined as an object or array in the TypeBox schema.
|
|
527
|
-
*/
|
|
528
|
-
isJsonbColumn(schema$1, columnName) {
|
|
529
|
-
const property = schema$1.properties[columnName];
|
|
530
|
-
if (!property) return false;
|
|
531
|
-
return property.type === "object" || property.type === "array";
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* Check if an array property contains primitive types (string, number, boolean, etc.)
|
|
535
|
-
* rather than objects. Primitive arrays should use native Drizzle operators.
|
|
536
|
-
* @returns true if the array contains primitives, false if it contains objects
|
|
537
|
-
*/
|
|
538
|
-
isPrimitiveArray(schema$1, columnName) {
|
|
539
|
-
const property = schema$1.properties[columnName];
|
|
540
|
-
if (!property || property.type !== "array") return false;
|
|
541
|
-
const items = property.items;
|
|
542
|
-
if (!items) return false;
|
|
543
|
-
const itemType = items.type;
|
|
544
|
-
return itemType === "string" || itemType === "number" || itemType === "integer" || itemType === "boolean" || itemType === "null";
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Get the type of a field by navigating through a schema path.
|
|
548
|
-
* Used for smart type casting in SQL queries.
|
|
549
|
-
*
|
|
550
|
-
* @param columnSchema The schema of the JSON column (e.g., t.object({ age: t.integer() }))
|
|
551
|
-
* @param path The path to navigate (e.g., ['contact', 'email'])
|
|
552
|
-
* @returns The type string (e.g., 'integer', 'number', 'string') or undefined if not found
|
|
553
|
-
*/
|
|
554
|
-
getFieldType(columnSchema, path) {
|
|
555
|
-
let current = columnSchema;
|
|
556
|
-
for (const segment of path) if (current.type === "object" && current.properties) {
|
|
557
|
-
current = current.properties[segment];
|
|
558
|
-
if (!current) return;
|
|
559
|
-
} else return;
|
|
560
|
-
return current.type;
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Check if a nested path points to an array property.
|
|
564
|
-
*/
|
|
565
|
-
isArrayProperty(schema$1, path) {
|
|
566
|
-
if (path.length === 0) return false;
|
|
567
|
-
let currentSchema = schema$1.properties[path[0]];
|
|
568
|
-
if (!currentSchema) return false;
|
|
569
|
-
if (currentSchema.type === "array") return true;
|
|
570
|
-
for (let i = 1; i < path.length; i++) if (currentSchema.type === "object" && currentSchema.properties) {
|
|
571
|
-
currentSchema = currentSchema.properties[path[i]];
|
|
572
|
-
if (!currentSchema) return false;
|
|
573
|
-
if (currentSchema.type === "array") return true;
|
|
574
|
-
} else return false;
|
|
575
|
-
return false;
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
|
|
579
366
|
//#endregion
|
|
580
367
|
//#region ../../src/orm/services/QueryManager.ts
|
|
581
368
|
var QueryManager = class {
|
|
582
|
-
jsonQueryManager = $inject(PgJsonQueryManager);
|
|
583
369
|
alepha = $inject(Alepha);
|
|
584
370
|
/**
|
|
585
371
|
* Convert a query object to a SQL query.
|
|
@@ -639,11 +425,7 @@ var QueryManager = class {
|
|
|
639
425
|
});
|
|
640
426
|
if (where) return not(where);
|
|
641
427
|
}
|
|
642
|
-
if (operator)
|
|
643
|
-
const column = col(key);
|
|
644
|
-
const jsonbSql = this.buildJsonbQuery(column, operator, schema$1, key, options.dialect);
|
|
645
|
-
if (jsonbSql) conditions.push(jsonbSql);
|
|
646
|
-
} else {
|
|
428
|
+
if (operator) {
|
|
647
429
|
const column = col(key);
|
|
648
430
|
const sql$2 = this.mapOperatorToSql(operator, column, schema$1, key, options.dialect);
|
|
649
431
|
if (sql$2) conditions.push(sql$2);
|
|
@@ -654,32 +436,6 @@ var QueryManager = class {
|
|
|
654
436
|
return and(...conditions);
|
|
655
437
|
}
|
|
656
438
|
/**
|
|
657
|
-
* Build a JSONB query for nested object/array queries.
|
|
658
|
-
*/
|
|
659
|
-
buildJsonbQuery(column, nestedQuery, schema$1, columnName, dialect) {
|
|
660
|
-
const queries = this.jsonQueryManager.parseNestedQuery(nestedQuery);
|
|
661
|
-
if (queries.length === 0) return;
|
|
662
|
-
const columnSchema = schema$1.properties[columnName];
|
|
663
|
-
const conditions = [];
|
|
664
|
-
for (const { path, operator } of queries) {
|
|
665
|
-
const isArrayOperator = operator.arrayContains !== void 0 || operator.arrayContained !== void 0 || operator.arrayOverlaps !== void 0;
|
|
666
|
-
const isArrayProp = this.jsonQueryManager.isArrayProperty(schema$1, [columnName, ...path]);
|
|
667
|
-
if (isArrayProp && isArrayOperator) {
|
|
668
|
-
const condition = this.jsonQueryManager.buildJsonbCondition(column, path, operator, dialect, columnSchema);
|
|
669
|
-
if (condition) conditions.push(condition);
|
|
670
|
-
} else if (isArrayProp && !isArrayOperator) {
|
|
671
|
-
const condition = this.jsonQueryManager.buildJsonbArrayCondition(column, path, "", operator, dialect);
|
|
672
|
-
if (condition) conditions.push(condition);
|
|
673
|
-
} else {
|
|
674
|
-
const condition = this.jsonQueryManager.buildJsonbCondition(column, path, operator, dialect, columnSchema);
|
|
675
|
-
if (condition) conditions.push(condition);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
if (conditions.length === 0) return;
|
|
679
|
-
if (conditions.length === 1) return conditions[0];
|
|
680
|
-
return and(...conditions);
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
439
|
* Check if an object has any filter operator properties.
|
|
684
440
|
*/
|
|
685
441
|
hasFilterOperatorProperties(obj) {
|
|
@@ -947,11 +703,11 @@ var Repository = class {
|
|
|
947
703
|
* Start a SELECT DISTINCT query on the table.
|
|
948
704
|
*/
|
|
949
705
|
rawSelectDistinct(opts = {}, columns = []) {
|
|
950
|
-
const db = opts.tx ?? this.db;
|
|
706
|
+
const db$1 = opts.tx ?? this.db;
|
|
951
707
|
const table = this.table;
|
|
952
708
|
const fields = {};
|
|
953
709
|
for (const column of columns) if (typeof column === "string") fields[column] = this.col(column);
|
|
954
|
-
return db.selectDistinct(fields).from(table);
|
|
710
|
+
return db$1.selectDistinct(fields).from(table);
|
|
955
711
|
}
|
|
956
712
|
/**
|
|
957
713
|
* Start an INSERT query on the table.
|
|
@@ -1690,6 +1446,22 @@ const devMigrationsSchema = t.object({
|
|
|
1690
1446
|
created_at: t.string()
|
|
1691
1447
|
});
|
|
1692
1448
|
|
|
1449
|
+
//#endregion
|
|
1450
|
+
//#region ../../src/orm/errors/DbMigrationError.ts
|
|
1451
|
+
var DbMigrationError = class extends DbError {
|
|
1452
|
+
name = "DbMigrationError";
|
|
1453
|
+
constructor(cause) {
|
|
1454
|
+
super("Failed to migrate database", cause);
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
//#endregion
|
|
1459
|
+
//#region ../../src/orm/types/byte.ts
|
|
1460
|
+
/**
|
|
1461
|
+
* Postgres bytea type.
|
|
1462
|
+
*/
|
|
1463
|
+
const byte = customType({ dataType: () => "bytea" });
|
|
1464
|
+
|
|
1693
1465
|
//#endregion
|
|
1694
1466
|
//#region ../../src/orm/services/ModelBuilder.ts
|
|
1695
1467
|
/**
|
|
@@ -1783,39 +1555,52 @@ var ModelBuilder = class {
|
|
|
1783
1555
|
};
|
|
1784
1556
|
|
|
1785
1557
|
//#endregion
|
|
1786
|
-
//#region ../../src/orm/services/
|
|
1787
|
-
var
|
|
1558
|
+
//#region ../../src/orm/services/PostgresModelBuilder.ts
|
|
1559
|
+
var PostgresModelBuilder = class extends ModelBuilder {
|
|
1560
|
+
schemas = /* @__PURE__ */ new Map();
|
|
1561
|
+
getPgSchema(name) {
|
|
1562
|
+
if (!this.schemas.has(name) && name !== "public") this.schemas.set(name, pgSchema(name));
|
|
1563
|
+
const nsp = name !== "public" ? this.schemas.get(name) : {
|
|
1564
|
+
enum: pgEnum,
|
|
1565
|
+
table: pgTable
|
|
1566
|
+
};
|
|
1567
|
+
if (!nsp) throw new AlephaError(`Postgres schema ${name} not found`);
|
|
1568
|
+
return nsp;
|
|
1569
|
+
}
|
|
1788
1570
|
buildTable(entity, options) {
|
|
1789
1571
|
const tableName = entity.name;
|
|
1790
1572
|
if (options.tables.has(tableName)) return;
|
|
1791
|
-
const
|
|
1573
|
+
const nsp = this.getPgSchema(options.schema);
|
|
1574
|
+
const columns = this.schemaToPgColumns(tableName, entity.schema, nsp, options.enums, options.tables);
|
|
1575
|
+
const configFn = this.getTableConfig(entity, options.tables);
|
|
1576
|
+
const table = nsp.table(tableName, columns, configFn);
|
|
1792
1577
|
options.tables.set(tableName, table);
|
|
1793
1578
|
}
|
|
1794
1579
|
buildSequence(sequence, options) {
|
|
1795
|
-
|
|
1580
|
+
const sequenceName = sequence.name;
|
|
1581
|
+
if (options.sequences.has(sequenceName)) return;
|
|
1582
|
+
const nsp = this.getPgSchema(options.schema);
|
|
1583
|
+
options.sequences.set(sequenceName, nsp.sequence(sequenceName, sequence.options));
|
|
1796
1584
|
}
|
|
1797
1585
|
/**
|
|
1798
|
-
* Get
|
|
1586
|
+
* Get PostgreSQL-specific config builder for the table.
|
|
1799
1587
|
*/
|
|
1800
1588
|
getTableConfig(entity, tables) {
|
|
1801
|
-
const
|
|
1802
|
-
index
|
|
1803
|
-
uniqueIndex
|
|
1804
|
-
unique
|
|
1805
|
-
check
|
|
1806
|
-
foreignKey
|
|
1589
|
+
const pgBuilders = {
|
|
1590
|
+
index,
|
|
1591
|
+
uniqueIndex,
|
|
1592
|
+
unique,
|
|
1593
|
+
check,
|
|
1594
|
+
foreignKey
|
|
1807
1595
|
};
|
|
1808
1596
|
const tableResolver = (entityName) => {
|
|
1809
1597
|
return tables.get(entityName);
|
|
1810
1598
|
};
|
|
1811
|
-
return this.buildTableConfig(entity,
|
|
1812
|
-
const customConfigs = config(self);
|
|
1813
|
-
return Array.isArray(customConfigs) ? customConfigs : [];
|
|
1814
|
-
});
|
|
1599
|
+
return this.buildTableConfig(entity, pgBuilders, tableResolver);
|
|
1815
1600
|
}
|
|
1816
|
-
|
|
1601
|
+
schemaToPgColumns = (tableName, schema$1, nsp, enums, tables) => {
|
|
1817
1602
|
return Object.entries(schema$1.properties).reduce((columns, [key, value]) => {
|
|
1818
|
-
let col = this.
|
|
1603
|
+
let col = this.mapFieldToColumn(tableName, key, value, nsp, enums);
|
|
1819
1604
|
if ("default" in value && value.default != null) col = col.default(value.default);
|
|
1820
1605
|
if (PG_PRIMARY_KEY in value) col = col.primaryKey();
|
|
1821
1606
|
if (PG_REF in value) {
|
|
@@ -1836,219 +1621,255 @@ var SqliteModelBuilder = class extends ModelBuilder {
|
|
|
1836
1621
|
};
|
|
1837
1622
|
}, {});
|
|
1838
1623
|
};
|
|
1839
|
-
|
|
1624
|
+
mapFieldToColumn = (tableName, fieldName, value, nsp, enums) => {
|
|
1840
1625
|
const key = this.toColumnName(fieldName);
|
|
1841
1626
|
if ("anyOf" in value && Array.isArray(value.anyOf) && value.anyOf.length === 2 && value.anyOf.some((it) => t.schema.isNull(it))) value = value.anyOf.find((it) => !t.schema.isNull(it));
|
|
1842
1627
|
if (t.schema.isInteger(value)) {
|
|
1843
|
-
if (PG_SERIAL in value
|
|
1628
|
+
if (PG_SERIAL in value) return pg$2.serial(key);
|
|
1629
|
+
if (PG_IDENTITY in value) {
|
|
1630
|
+
const options = value[PG_IDENTITY];
|
|
1631
|
+
if (options.mode === "byDefault") return pg$2.integer().generatedByDefaultAsIdentity(options);
|
|
1632
|
+
return pg$2.integer().generatedAlwaysAsIdentity(options);
|
|
1633
|
+
}
|
|
1844
1634
|
return pg$2.integer(key);
|
|
1845
1635
|
}
|
|
1846
1636
|
if (t.schema.isBigInt(value)) {
|
|
1847
|
-
if (
|
|
1848
|
-
|
|
1637
|
+
if (PG_IDENTITY in value) {
|
|
1638
|
+
const options = value[PG_IDENTITY];
|
|
1639
|
+
if (options.mode === "byDefault") return pg$2.bigint({ mode: "bigint" }).generatedByDefaultAsIdentity(options);
|
|
1640
|
+
return pg$2.bigint({ mode: "bigint" }).generatedAlwaysAsIdentity(options);
|
|
1641
|
+
}
|
|
1849
1642
|
}
|
|
1850
1643
|
if (t.schema.isNumber(value)) {
|
|
1851
|
-
if (PG_IDENTITY in value)
|
|
1644
|
+
if (PG_IDENTITY in value) {
|
|
1645
|
+
const options = value[PG_IDENTITY];
|
|
1646
|
+
if (options.mode === "byDefault") return pg$2.bigint({ mode: "number" }).generatedByDefaultAsIdentity(options);
|
|
1647
|
+
return pg$2.bigint({ mode: "number" }).generatedAlwaysAsIdentity(options);
|
|
1648
|
+
}
|
|
1649
|
+
if (value.format === "int64") return pg$2.bigint(key, { mode: "number" });
|
|
1852
1650
|
return pg$2.numeric(key);
|
|
1853
1651
|
}
|
|
1854
|
-
if (t.schema.isString(value)) return this.
|
|
1855
|
-
if (t.schema.isBoolean(value)) return
|
|
1856
|
-
if (t.schema.isObject(value)) return
|
|
1857
|
-
if (t.schema.isRecord(value)) return
|
|
1858
|
-
|
|
1652
|
+
if (t.schema.isString(value)) return this.mapStringToColumn(key, value);
|
|
1653
|
+
if (t.schema.isBoolean(value)) return pg$2.boolean(key);
|
|
1654
|
+
if (t.schema.isObject(value)) return schema(key, value);
|
|
1655
|
+
if (t.schema.isRecord(value)) return schema(key, value);
|
|
1656
|
+
const isTypeEnum = (value$1) => t.schema.isUnsafe(value$1) && "type" in value$1 && value$1.type === "string" && "enum" in value$1 && Array.isArray(value$1.enum);
|
|
1859
1657
|
if (t.schema.isArray(value)) {
|
|
1860
|
-
if (t.schema.isObject(value.items)) return
|
|
1861
|
-
if (t.schema.isRecord(value.items)) return
|
|
1862
|
-
if (t.schema.
|
|
1863
|
-
if (t.schema.
|
|
1864
|
-
if (t.schema.
|
|
1865
|
-
if (t.schema.
|
|
1866
|
-
if (
|
|
1658
|
+
if (t.schema.isObject(value.items)) return schema(key, value);
|
|
1659
|
+
if (t.schema.isRecord(value.items)) return schema(key, value);
|
|
1660
|
+
if (t.schema.isString(value.items)) return pg$2.text(key).array();
|
|
1661
|
+
if (t.schema.isInteger(value.items)) return pg$2.integer(key).array();
|
|
1662
|
+
if (t.schema.isNumber(value.items)) return pg$2.numeric(key).array();
|
|
1663
|
+
if (t.schema.isBoolean(value.items)) return pg$2.boolean(key).array();
|
|
1664
|
+
if (isTypeEnum(value.items)) return pg$2.text(key).array();
|
|
1867
1665
|
}
|
|
1868
|
-
if (
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1666
|
+
if (isTypeEnum(value)) {
|
|
1667
|
+
if (!value.enum.every((it) => typeof it === "string")) throw new AlephaError(`Enum for ${fieldName} must be an array of strings, got ${JSON.stringify(value.enum)}`);
|
|
1668
|
+
if (PG_ENUM in value && value[PG_ENUM]) {
|
|
1669
|
+
const enumName = value[PG_ENUM].name ?? `${tableName}_${key}_enum`;
|
|
1670
|
+
if (enums.has(enumName)) {
|
|
1671
|
+
const values = enums.get(enumName).enumValues.join(",");
|
|
1672
|
+
const newValues = value.enum.join(",");
|
|
1673
|
+
if (values !== newValues) throw new AlephaError(`Enum name conflict for ${enumName}: [${values}] vs [${newValues}]`);
|
|
1674
|
+
}
|
|
1675
|
+
enums.set(enumName, nsp.enum(enumName, value.enum));
|
|
1676
|
+
return enums.get(enumName)(key);
|
|
1677
|
+
}
|
|
1678
|
+
return this.mapStringToColumn(key, value);
|
|
1875
1679
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1680
|
+
throw new AlephaError(`Unsupported schema type for ${fieldName} as ${JSON.stringify(value)}`);
|
|
1681
|
+
};
|
|
1682
|
+
/**
|
|
1683
|
+
* Map a string to a PG column.
|
|
1684
|
+
*
|
|
1685
|
+
* @param key The key of the field.
|
|
1686
|
+
* @param value The value of the field.
|
|
1687
|
+
*/
|
|
1688
|
+
mapStringToColumn = (key, value) => {
|
|
1689
|
+
if ("format" in value) {
|
|
1690
|
+
if (value.format === "uuid") {
|
|
1691
|
+
if (PG_PRIMARY_KEY in value) return pg$2.uuid(key).defaultRandom();
|
|
1692
|
+
return pg$2.uuid(key);
|
|
1693
|
+
}
|
|
1694
|
+
if (value.format === "byte") return byte(key);
|
|
1695
|
+
if (value.format === "date-time") {
|
|
1696
|
+
if (PG_CREATED_AT in value) return pg$2.timestamp(key, {
|
|
1697
|
+
mode: "string",
|
|
1698
|
+
withTimezone: true
|
|
1699
|
+
}).defaultNow();
|
|
1700
|
+
if (PG_UPDATED_AT in value) return pg$2.timestamp(key, {
|
|
1701
|
+
mode: "string",
|
|
1702
|
+
withTimezone: true
|
|
1703
|
+
}).defaultNow();
|
|
1704
|
+
return pg$2.timestamp(key, {
|
|
1705
|
+
mode: "string",
|
|
1706
|
+
withTimezone: true
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
if (value.format === "date") return pg$2.date(key, { mode: "string" });
|
|
1881
1710
|
}
|
|
1882
|
-
if (value.format === "date") return this.sqliteDate(key, {});
|
|
1883
1711
|
return pg$2.text(key);
|
|
1884
1712
|
};
|
|
1885
|
-
sqliteJson = (name, document) => pg$2.customType({
|
|
1886
|
-
dataType: () => "text",
|
|
1887
|
-
toDriver: (value) => JSON.stringify(value),
|
|
1888
|
-
fromDriver: (value) => {
|
|
1889
|
-
return value && typeof value === "string" ? JSON.parse(value) : value;
|
|
1890
|
-
}
|
|
1891
|
-
})(name, { document }).$type();
|
|
1892
|
-
sqliteDateTime = pg$2.customType({
|
|
1893
|
-
dataType: () => "integer",
|
|
1894
|
-
toDriver: (value) => new Date(value).getTime(),
|
|
1895
|
-
fromDriver: (value) => {
|
|
1896
|
-
return new Date(value).toISOString();
|
|
1897
|
-
}
|
|
1898
|
-
});
|
|
1899
|
-
sqliteBool = pg$2.customType({
|
|
1900
|
-
dataType: () => "integer",
|
|
1901
|
-
toDriver: (value) => value ? 1 : 0,
|
|
1902
|
-
fromDriver: (value) => value === 1
|
|
1903
|
-
});
|
|
1904
|
-
sqliteDate = pg$2.customType({
|
|
1905
|
-
dataType: () => "integer",
|
|
1906
|
-
toDriver: (value) => new Date(value).getTime(),
|
|
1907
|
-
fromDriver: (value) => {
|
|
1908
|
-
return new Date(value).toISOString().split("T")[0];
|
|
1909
|
-
}
|
|
1910
|
-
});
|
|
1911
1713
|
};
|
|
1912
1714
|
|
|
1913
1715
|
//#endregion
|
|
1914
|
-
//#region ../../src/orm/providers/drivers/
|
|
1716
|
+
//#region ../../src/orm/providers/drivers/BunPostgresProvider.ts
|
|
1717
|
+
const envSchema$4 = t.object({
|
|
1718
|
+
DATABASE_URL: t.optional(t.text()),
|
|
1719
|
+
POSTGRES_SCHEMA: t.optional(t.text())
|
|
1720
|
+
});
|
|
1915
1721
|
/**
|
|
1916
|
-
*
|
|
1722
|
+
* Bun PostgreSQL provider using Drizzle ORM with Bun's native SQL client.
|
|
1917
1723
|
*
|
|
1918
|
-
* This provider
|
|
1919
|
-
*
|
|
1724
|
+
* This provider uses Bun's built-in SQL class for PostgreSQL connections,
|
|
1725
|
+
* which provides excellent performance on the Bun runtime.
|
|
1920
1726
|
*
|
|
1921
1727
|
* @example
|
|
1922
1728
|
* ```ts
|
|
1923
|
-
* //
|
|
1924
|
-
*
|
|
1729
|
+
* // Set DATABASE_URL environment variable
|
|
1730
|
+
* // DATABASE_URL=postgres://user:password@localhost:5432/database
|
|
1731
|
+
*
|
|
1732
|
+
* // Or configure programmatically
|
|
1733
|
+
* alepha.with({
|
|
1734
|
+
* provide: DatabaseProvider,
|
|
1735
|
+
* use: BunPostgresProvider,
|
|
1736
|
+
* });
|
|
1925
1737
|
* ```
|
|
1926
1738
|
*/
|
|
1927
|
-
var
|
|
1928
|
-
kit = $inject(DrizzleKitProvider);
|
|
1739
|
+
var BunPostgresProvider = class extends DatabaseProvider {
|
|
1929
1740
|
log = $logger();
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1741
|
+
env = $env(envSchema$4);
|
|
1742
|
+
kit = $inject(DrizzleKitProvider);
|
|
1743
|
+
builder = $inject(PostgresModelBuilder);
|
|
1744
|
+
client;
|
|
1745
|
+
bunDb;
|
|
1746
|
+
dialect = "postgresql";
|
|
1934
1747
|
get name() {
|
|
1935
|
-
return "
|
|
1748
|
+
return "bun-postgres";
|
|
1936
1749
|
}
|
|
1937
|
-
|
|
1750
|
+
/**
|
|
1751
|
+
* In testing mode, the schema name will be generated and deleted after the test.
|
|
1752
|
+
*/
|
|
1753
|
+
schemaForTesting = this.alepha.isTest() ? this.env.POSTGRES_SCHEMA?.startsWith("test_") ? this.env.POSTGRES_SCHEMA : this.generateTestSchemaName() : void 0;
|
|
1938
1754
|
get url() {
|
|
1755
|
+
if (!this.env.DATABASE_URL) throw new AlephaError("DATABASE_URL is not defined in the environment");
|
|
1939
1756
|
return this.env.DATABASE_URL;
|
|
1940
1757
|
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Execute a SQL statement.
|
|
1760
|
+
*/
|
|
1761
|
+
execute(statement) {
|
|
1762
|
+
try {
|
|
1763
|
+
return this.db.execute(statement);
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
throw new DbError("Error executing statement", error);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Get Postgres schema used by this provider.
|
|
1770
|
+
*/
|
|
1771
|
+
get schema() {
|
|
1772
|
+
if (this.schemaForTesting) return this.schemaForTesting;
|
|
1773
|
+
if (this.env.POSTGRES_SCHEMA) return this.env.POSTGRES_SCHEMA;
|
|
1774
|
+
return "public";
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Get the Drizzle Postgres database instance.
|
|
1778
|
+
*/
|
|
1941
1779
|
get db() {
|
|
1942
|
-
if (!this.
|
|
1943
|
-
return this.
|
|
1780
|
+
if (!this.bunDb) throw new AlephaError("Database not initialized");
|
|
1781
|
+
return this.bunDb;
|
|
1944
1782
|
}
|
|
1945
|
-
async
|
|
1946
|
-
const {
|
|
1947
|
-
|
|
1783
|
+
async executeMigrations(migrationsFolder) {
|
|
1784
|
+
const { migrate: migrate$3 } = await import("drizzle-orm/bun-sql/migrator");
|
|
1785
|
+
await migrate$3(this.bunDb, { migrationsFolder });
|
|
1948
1786
|
}
|
|
1949
1787
|
onStart = $hook({
|
|
1950
1788
|
on: "start",
|
|
1951
1789
|
handler: async () => {
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
const { drizzle: drizzle$3 } = await import("drizzle-orm/d1");
|
|
1959
|
-
this.drizzleDb = drizzle$3(this.d1);
|
|
1960
|
-
await this.migrateDatabase();
|
|
1961
|
-
this.log.info("Using Cloudflare D1 database");
|
|
1790
|
+
await this.connect();
|
|
1791
|
+
if (!this.alepha.isServerless()) try {
|
|
1792
|
+
await this.migrate.run();
|
|
1793
|
+
} catch (error) {
|
|
1794
|
+
throw new DbMigrationError(error);
|
|
1795
|
+
}
|
|
1962
1796
|
}
|
|
1963
1797
|
});
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
}
|
|
1975
|
-
/**
|
|
1976
|
-
* Override test migration to run migrations instead of sync.
|
|
1977
|
-
* D1 doesn't support schema synchronization.
|
|
1978
|
-
*/
|
|
1979
|
-
async runTestMigration() {
|
|
1980
|
-
const migrationsFolder = this.getMigrationsFolder();
|
|
1981
|
-
try {
|
|
1982
|
-
await this.executeMigrations(migrationsFolder);
|
|
1983
|
-
} catch {
|
|
1984
|
-
this.log.warn("D1 migrations failed in test environment - ensure migrations exist");
|
|
1798
|
+
onStop = $hook({
|
|
1799
|
+
on: "stop",
|
|
1800
|
+
handler: async () => {
|
|
1801
|
+
if (this.alepha.isTest() && this.schemaForTesting && this.schemaForTesting.startsWith("test_")) {
|
|
1802
|
+
if (!/^test_[a-z0-9_]+$/i.test(this.schemaForTesting)) throw new AlephaError(`Invalid test schema name: ${this.schemaForTesting}. Must match pattern: test_[a-z0-9_]+`);
|
|
1803
|
+
this.log.warn(`Deleting test schema '${this.schemaForTesting}' ...`);
|
|
1804
|
+
await this.execute(sql$1`DROP SCHEMA IF EXISTS ${sql$1.raw(this.schemaForTesting)} CASCADE`);
|
|
1805
|
+
this.log.info(`Test schema '${this.schemaForTesting}' deleted`);
|
|
1806
|
+
}
|
|
1807
|
+
await this.close();
|
|
1985
1808
|
}
|
|
1809
|
+
});
|
|
1810
|
+
async connect() {
|
|
1811
|
+
this.log.debug("Connect ..");
|
|
1812
|
+
if (typeof Bun === "undefined") throw new AlephaError("BunPostgresProvider requires the Bun runtime. Use NodePostgresProvider for Node.js.");
|
|
1813
|
+
const { drizzle: drizzle$3 } = await import("drizzle-orm/bun-sql");
|
|
1814
|
+
const { SQL: SQL$1 } = await import("bun");
|
|
1815
|
+
this.client = new SQL$1(this.url);
|
|
1816
|
+
await this.client.unsafe("SELECT 1");
|
|
1817
|
+
this.bunDb = drizzle$3({
|
|
1818
|
+
client: this.client,
|
|
1819
|
+
logger: { logQuery: (query, params) => {
|
|
1820
|
+
this.log.trace(query, { params });
|
|
1821
|
+
} }
|
|
1822
|
+
});
|
|
1823
|
+
this.log.info("Connection OK");
|
|
1986
1824
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1825
|
+
async close() {
|
|
1826
|
+
if (this.client) {
|
|
1827
|
+
this.log.debug("Close...");
|
|
1828
|
+
await this.client.close();
|
|
1829
|
+
this.client = void 0;
|
|
1830
|
+
this.bunDb = void 0;
|
|
1831
|
+
this.log.info("Connection closed");
|
|
1832
|
+
}
|
|
1995
1833
|
}
|
|
1834
|
+
migrate = $lock({ handler: async () => {
|
|
1835
|
+
await this.migrateDatabase();
|
|
1836
|
+
} });
|
|
1996
1837
|
};
|
|
1997
1838
|
|
|
1998
1839
|
//#endregion
|
|
1999
|
-
//#region ../../src/orm/
|
|
2000
|
-
|
|
2001
|
-
* Postgres bytea type.
|
|
2002
|
-
*/
|
|
2003
|
-
const byte = customType({ dataType: () => "bytea" });
|
|
2004
|
-
|
|
2005
|
-
//#endregion
|
|
2006
|
-
//#region ../../src/orm/services/PostgresModelBuilder.ts
|
|
2007
|
-
var PostgresModelBuilder = class extends ModelBuilder {
|
|
2008
|
-
schemas = /* @__PURE__ */ new Map();
|
|
2009
|
-
getPgSchema(name) {
|
|
2010
|
-
if (!this.schemas.has(name) && name !== "public") this.schemas.set(name, pgSchema(name));
|
|
2011
|
-
const nsp = name !== "public" ? this.schemas.get(name) : {
|
|
2012
|
-
enum: pgEnum,
|
|
2013
|
-
table: pgTable
|
|
2014
|
-
};
|
|
2015
|
-
if (!nsp) throw new AlephaError(`Postgres schema ${name} not found`);
|
|
2016
|
-
return nsp;
|
|
2017
|
-
}
|
|
1840
|
+
//#region ../../src/orm/services/SqliteModelBuilder.ts
|
|
1841
|
+
var SqliteModelBuilder = class extends ModelBuilder {
|
|
2018
1842
|
buildTable(entity, options) {
|
|
2019
1843
|
const tableName = entity.name;
|
|
2020
1844
|
if (options.tables.has(tableName)) return;
|
|
2021
|
-
const
|
|
2022
|
-
const columns = this.schemaToPgColumns(tableName, entity.schema, nsp, options.enums, options.tables);
|
|
2023
|
-
const configFn = this.getTableConfig(entity, options.tables);
|
|
2024
|
-
const table = nsp.table(tableName, columns, configFn);
|
|
1845
|
+
const table = sqliteTable(tableName, this.schemaToSqliteColumns(tableName, entity.schema, options.enums, options.tables), this.getTableConfig(entity, options.tables));
|
|
2025
1846
|
options.tables.set(tableName, table);
|
|
2026
1847
|
}
|
|
2027
1848
|
buildSequence(sequence, options) {
|
|
2028
|
-
|
|
2029
|
-
if (options.sequences.has(sequenceName)) return;
|
|
2030
|
-
const nsp = this.getPgSchema(options.schema);
|
|
2031
|
-
options.sequences.set(sequenceName, nsp.sequence(sequenceName, sequence.options));
|
|
1849
|
+
throw new AlephaError("SQLite does not support sequences");
|
|
2032
1850
|
}
|
|
2033
1851
|
/**
|
|
2034
|
-
* Get
|
|
1852
|
+
* Get SQLite-specific config builder for the table.
|
|
2035
1853
|
*/
|
|
2036
1854
|
getTableConfig(entity, tables) {
|
|
2037
|
-
const
|
|
2038
|
-
index,
|
|
2039
|
-
uniqueIndex,
|
|
2040
|
-
unique,
|
|
2041
|
-
check,
|
|
2042
|
-
foreignKey
|
|
1855
|
+
const sqliteBuilders = {
|
|
1856
|
+
index: index$1,
|
|
1857
|
+
uniqueIndex: uniqueIndex$1,
|
|
1858
|
+
unique: unique$1,
|
|
1859
|
+
check: check$1,
|
|
1860
|
+
foreignKey: foreignKey$1
|
|
2043
1861
|
};
|
|
2044
1862
|
const tableResolver = (entityName) => {
|
|
2045
1863
|
return tables.get(entityName);
|
|
2046
1864
|
};
|
|
2047
|
-
return this.buildTableConfig(entity,
|
|
1865
|
+
return this.buildTableConfig(entity, sqliteBuilders, tableResolver, (config, self) => {
|
|
1866
|
+
const customConfigs = config(self);
|
|
1867
|
+
return Array.isArray(customConfigs) ? customConfigs : [];
|
|
1868
|
+
});
|
|
2048
1869
|
}
|
|
2049
|
-
|
|
1870
|
+
schemaToSqliteColumns = (tableName, schema$1, enums, tables) => {
|
|
2050
1871
|
return Object.entries(schema$1.properties).reduce((columns, [key, value]) => {
|
|
2051
|
-
let col = this.
|
|
1872
|
+
let col = this.mapFieldToSqliteColumn(tableName, key, value, enums);
|
|
2052
1873
|
if ("default" in value && value.default != null) col = col.default(value.default);
|
|
2053
1874
|
if (PG_PRIMARY_KEY in value) col = col.primaryKey();
|
|
2054
1875
|
if (PG_REF in value) {
|
|
@@ -2069,95 +1890,257 @@ var PostgresModelBuilder = class extends ModelBuilder {
|
|
|
2069
1890
|
};
|
|
2070
1891
|
}, {});
|
|
2071
1892
|
};
|
|
2072
|
-
|
|
1893
|
+
mapFieldToSqliteColumn = (tableName, fieldName, value, enums) => {
|
|
2073
1894
|
const key = this.toColumnName(fieldName);
|
|
2074
1895
|
if ("anyOf" in value && Array.isArray(value.anyOf) && value.anyOf.length === 2 && value.anyOf.some((it) => t.schema.isNull(it))) value = value.anyOf.find((it) => !t.schema.isNull(it));
|
|
2075
1896
|
if (t.schema.isInteger(value)) {
|
|
2076
|
-
if (PG_SERIAL in value) return pg$1.
|
|
2077
|
-
if (PG_IDENTITY in value) {
|
|
2078
|
-
const options = value[PG_IDENTITY];
|
|
2079
|
-
if (options.mode === "byDefault") return pg$1.integer().generatedByDefaultAsIdentity(options);
|
|
2080
|
-
return pg$1.integer().generatedAlwaysAsIdentity(options);
|
|
2081
|
-
}
|
|
1897
|
+
if (PG_SERIAL in value || PG_IDENTITY in value) return pg$1.integer(key, { mode: "number" }).primaryKey({ autoIncrement: true });
|
|
2082
1898
|
return pg$1.integer(key);
|
|
2083
1899
|
}
|
|
2084
1900
|
if (t.schema.isBigInt(value)) {
|
|
2085
|
-
if (PG_IDENTITY in value) {
|
|
2086
|
-
|
|
2087
|
-
if (options.mode === "byDefault") return pg$1.bigint({ mode: "bigint" }).generatedByDefaultAsIdentity(options);
|
|
2088
|
-
return pg$1.bigint({ mode: "bigint" }).generatedAlwaysAsIdentity(options);
|
|
2089
|
-
}
|
|
1901
|
+
if (PG_PRIMARY_KEY in value || PG_IDENTITY in value) return pg$1.integer(key, { mode: "number" }).primaryKey({ autoIncrement: true });
|
|
1902
|
+
return pg$1.integer(key, { mode: "number" });
|
|
2090
1903
|
}
|
|
2091
1904
|
if (t.schema.isNumber(value)) {
|
|
2092
|
-
if (PG_IDENTITY in value) {
|
|
2093
|
-
const options = value[PG_IDENTITY];
|
|
2094
|
-
if (options.mode === "byDefault") return pg$1.bigint({ mode: "number" }).generatedByDefaultAsIdentity(options);
|
|
2095
|
-
return pg$1.bigint({ mode: "number" }).generatedAlwaysAsIdentity(options);
|
|
2096
|
-
}
|
|
2097
|
-
if (value.format === "int64") return pg$1.bigint(key, { mode: "number" });
|
|
1905
|
+
if (PG_IDENTITY in value) return pg$1.integer(key, { mode: "number" }).primaryKey({ autoIncrement: true });
|
|
2098
1906
|
return pg$1.numeric(key);
|
|
2099
1907
|
}
|
|
2100
|
-
if (t.schema.isString(value)) return this.
|
|
2101
|
-
if (t.schema.isBoolean(value)) return
|
|
2102
|
-
if (t.schema.isObject(value)) return
|
|
2103
|
-
if (t.schema.isRecord(value)) return
|
|
2104
|
-
|
|
1908
|
+
if (t.schema.isString(value)) return this.mapStringToSqliteColumn(key, value);
|
|
1909
|
+
if (t.schema.isBoolean(value)) return this.sqliteBool(key, value);
|
|
1910
|
+
if (t.schema.isObject(value)) return this.sqliteJson(key, value);
|
|
1911
|
+
if (t.schema.isRecord(value)) return this.sqliteJson(key, value);
|
|
1912
|
+
if (t.schema.isAny(value)) return this.sqliteJson(key, value);
|
|
2105
1913
|
if (t.schema.isArray(value)) {
|
|
2106
|
-
if (t.schema.isObject(value.items)) return
|
|
2107
|
-
if (t.schema.isRecord(value.items)) return
|
|
2108
|
-
if (t.schema.
|
|
2109
|
-
if (t.schema.
|
|
2110
|
-
if (t.schema.
|
|
2111
|
-
if (t.schema.
|
|
2112
|
-
if (
|
|
1914
|
+
if (t.schema.isObject(value.items)) return this.sqliteJson(key, value);
|
|
1915
|
+
if (t.schema.isRecord(value.items)) return this.sqliteJson(key, value);
|
|
1916
|
+
if (t.schema.isAny(value.items)) return this.sqliteJson(key, value);
|
|
1917
|
+
if (t.schema.isString(value.items)) return this.sqliteJson(key, value);
|
|
1918
|
+
if (t.schema.isInteger(value.items)) return this.sqliteJson(key, value);
|
|
1919
|
+
if (t.schema.isNumber(value.items)) return this.sqliteJson(key, value);
|
|
1920
|
+
if (t.schema.isBoolean(value.items)) return this.sqliteJson(key, value);
|
|
2113
1921
|
}
|
|
2114
|
-
if (
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
if (values !== newValues) throw new AlephaError(`Enum name conflict for ${enumName}: [${values}] vs [${newValues}]`);
|
|
2122
|
-
}
|
|
2123
|
-
enums.set(enumName, nsp.enum(enumName, value.enum));
|
|
2124
|
-
return enums.get(enumName)(key);
|
|
2125
|
-
}
|
|
2126
|
-
return this.mapStringToColumn(key, value);
|
|
1922
|
+
if (t.schema.isUnsafe(value) && "type" in value && value.type === "string") return this.mapStringToSqliteColumn(key, value);
|
|
1923
|
+
throw new Error(`Unsupported schema for field '${tableName}.${fieldName}' (schema: ${JSON.stringify(value)})`);
|
|
1924
|
+
};
|
|
1925
|
+
mapStringToSqliteColumn = (key, value) => {
|
|
1926
|
+
if (value.format === "uuid") {
|
|
1927
|
+
if (PG_PRIMARY_KEY in value) return pg$1.text(key).primaryKey().$defaultFn(() => randomUUID());
|
|
1928
|
+
return pg$1.text(key);
|
|
2127
1929
|
}
|
|
2128
|
-
|
|
1930
|
+
if (value.format === "byte") return this.sqliteJson(key, value);
|
|
1931
|
+
if (value.format === "date-time") {
|
|
1932
|
+
if (PG_CREATED_AT in value) return this.sqliteDateTime(key, {}).default(sql$1`(unixepoch('subsec') * 1000)`);
|
|
1933
|
+
if (PG_UPDATED_AT in value) return this.sqliteDateTime(key, {}).default(sql$1`(unixepoch('subsec') * 1000)`);
|
|
1934
|
+
return this.sqliteDateTime(key, {});
|
|
1935
|
+
}
|
|
1936
|
+
if (value.format === "date") return this.sqliteDate(key, {});
|
|
1937
|
+
return pg$1.text(key);
|
|
2129
1938
|
};
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
1939
|
+
sqliteJson = (name, document) => pg$1.customType({
|
|
1940
|
+
dataType: () => "text",
|
|
1941
|
+
toDriver: (value) => JSON.stringify(value),
|
|
1942
|
+
fromDriver: (value) => {
|
|
1943
|
+
return value && typeof value === "string" ? JSON.parse(value) : value;
|
|
1944
|
+
}
|
|
1945
|
+
})(name, { document }).$type();
|
|
1946
|
+
sqliteDateTime = pg$1.customType({
|
|
1947
|
+
dataType: () => "integer",
|
|
1948
|
+
toDriver: (value) => new Date(value).getTime(),
|
|
1949
|
+
fromDriver: (value) => {
|
|
1950
|
+
return new Date(value).toISOString();
|
|
1951
|
+
}
|
|
1952
|
+
});
|
|
1953
|
+
sqliteBool = pg$1.customType({
|
|
1954
|
+
dataType: () => "integer",
|
|
1955
|
+
toDriver: (value) => value ? 1 : 0,
|
|
1956
|
+
fromDriver: (value) => value === 1
|
|
1957
|
+
});
|
|
1958
|
+
sqliteDate = pg$1.customType({
|
|
1959
|
+
dataType: () => "integer",
|
|
1960
|
+
toDriver: (value) => new Date(value).getTime(),
|
|
1961
|
+
fromDriver: (value) => {
|
|
1962
|
+
return new Date(value).toISOString().split("T")[0];
|
|
1963
|
+
}
|
|
1964
|
+
});
|
|
1965
|
+
};
|
|
1966
|
+
|
|
1967
|
+
//#endregion
|
|
1968
|
+
//#region ../../src/orm/providers/drivers/BunSqliteProvider.ts
|
|
1969
|
+
const envSchema$3 = t.object({ DATABASE_URL: t.optional(t.text()) });
|
|
1970
|
+
/**
|
|
1971
|
+
* Configuration options for the Bun SQLite database provider.
|
|
1972
|
+
*/
|
|
1973
|
+
const bunSqliteOptions = $atom({
|
|
1974
|
+
name: "alepha.postgres.bun-sqlite.options",
|
|
1975
|
+
schema: t.object({ path: t.optional(t.string({ description: "Filepath or :memory:. If empty, provider will use DATABASE_URL from env." })) }),
|
|
1976
|
+
default: {}
|
|
1977
|
+
});
|
|
1978
|
+
/**
|
|
1979
|
+
* Bun SQLite provider using Drizzle ORM with Bun's native SQLite client.
|
|
1980
|
+
*
|
|
1981
|
+
* This provider uses Bun's built-in `bun:sqlite` for SQLite connections,
|
|
1982
|
+
* which provides excellent performance on the Bun runtime.
|
|
1983
|
+
*
|
|
1984
|
+
* @example
|
|
1985
|
+
* ```ts
|
|
1986
|
+
* // Set DATABASE_URL environment variable
|
|
1987
|
+
* // DATABASE_URL=sqlite://./my-database.db
|
|
1988
|
+
*
|
|
1989
|
+
* // Or configure programmatically
|
|
1990
|
+
* alepha.with({
|
|
1991
|
+
* provide: DatabaseProvider,
|
|
1992
|
+
* use: BunSqliteProvider,
|
|
1993
|
+
* });
|
|
1994
|
+
*
|
|
1995
|
+
* // Or use options atom
|
|
1996
|
+
* alepha.store.mut(bunSqliteOptions, (old) => ({
|
|
1997
|
+
* ...old,
|
|
1998
|
+
* path: ":memory:",
|
|
1999
|
+
* }));
|
|
2000
|
+
* ```
|
|
2001
|
+
*/
|
|
2002
|
+
var BunSqliteProvider = class extends DatabaseProvider {
|
|
2003
|
+
kit = $inject(DrizzleKitProvider);
|
|
2004
|
+
log = $logger();
|
|
2005
|
+
env = $env(envSchema$3);
|
|
2006
|
+
builder = $inject(SqliteModelBuilder);
|
|
2007
|
+
options = $use(bunSqliteOptions);
|
|
2008
|
+
sqlite;
|
|
2009
|
+
bunDb;
|
|
2010
|
+
get name() {
|
|
2011
|
+
return "bun-sqlite";
|
|
2012
|
+
}
|
|
2013
|
+
dialect = "sqlite";
|
|
2014
|
+
get url() {
|
|
2015
|
+
const path = this.options.path ?? this.env.DATABASE_URL;
|
|
2016
|
+
if (path) {
|
|
2017
|
+
if (path.startsWith("postgres://")) throw new AlephaError("Postgres URL is not supported for SQLite provider.");
|
|
2018
|
+
return path;
|
|
2019
|
+
}
|
|
2020
|
+
if (this.alepha.isTest() || this.alepha.isServerless()) return ":memory:";
|
|
2021
|
+
else return "node_modules/.alepha/bun-sqlite.db";
|
|
2022
|
+
}
|
|
2023
|
+
get db() {
|
|
2024
|
+
if (!this.bunDb) throw new AlephaError("Database not initialized");
|
|
2025
|
+
return this.bunDb;
|
|
2026
|
+
}
|
|
2027
|
+
async execute(query) {
|
|
2028
|
+
return this.bunDb.all(query);
|
|
2029
|
+
}
|
|
2030
|
+
onStart = $hook({
|
|
2031
|
+
on: "start",
|
|
2032
|
+
handler: async () => {
|
|
2033
|
+
if (typeof Bun === "undefined") throw new AlephaError("BunSqliteProvider requires the Bun runtime. Use NodeSqliteProvider for Node.js.");
|
|
2034
|
+
const { Database } = await import("bun:sqlite");
|
|
2035
|
+
const { drizzle: drizzle$3 } = await import("drizzle-orm/bun-sqlite");
|
|
2036
|
+
const filepath = this.url.replace("sqlite://", "").replace("sqlite:", "");
|
|
2037
|
+
if (filepath !== ":memory:" && filepath !== "") {
|
|
2038
|
+
const dirname = filepath.split("/").slice(0, -1).join("/");
|
|
2039
|
+
if (dirname) await mkdir(dirname, { recursive: true }).catch(() => null);
|
|
2141
2040
|
}
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2041
|
+
this.sqlite = new Database(filepath);
|
|
2042
|
+
this.bunDb = drizzle$3({
|
|
2043
|
+
client: this.sqlite,
|
|
2044
|
+
logger: { logQuery: (query, params) => {
|
|
2045
|
+
this.log.trace(query, { params });
|
|
2046
|
+
} }
|
|
2047
|
+
});
|
|
2048
|
+
await this.migrateDatabase();
|
|
2049
|
+
this.log.info(`Using Bun SQLite database at ${filepath}`);
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
onStop = $hook({
|
|
2053
|
+
on: "stop",
|
|
2054
|
+
handler: async () => {
|
|
2055
|
+
if (this.sqlite) {
|
|
2056
|
+
this.log.debug("Closing Bun SQLite connection...");
|
|
2057
|
+
this.sqlite.close();
|
|
2058
|
+
this.sqlite = void 0;
|
|
2059
|
+
this.bunDb = void 0;
|
|
2060
|
+
this.log.info("Bun SQLite connection closed");
|
|
2156
2061
|
}
|
|
2157
|
-
if (value.format === "date") return pg$1.date(key, { mode: "string" });
|
|
2158
2062
|
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2063
|
+
});
|
|
2064
|
+
async executeMigrations(migrationsFolder) {
|
|
2065
|
+
const { migrate: migrate$3 } = await import("drizzle-orm/bun-sqlite/migrator");
|
|
2066
|
+
await migrate$3(this.bunDb, { migrationsFolder });
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2069
|
+
|
|
2070
|
+
//#endregion
|
|
2071
|
+
//#region ../../src/orm/providers/drivers/CloudflareD1Provider.ts
|
|
2072
|
+
/**
|
|
2073
|
+
* Cloudflare D1 SQLite provider using Drizzle ORM.
|
|
2074
|
+
*
|
|
2075
|
+
* This provider requires a D1 binding to be set via `cloudflareD1Options` before starting.
|
|
2076
|
+
* The binding is typically obtained from the Cloudflare Workers environment.
|
|
2077
|
+
*
|
|
2078
|
+
* @example
|
|
2079
|
+
* ```ts
|
|
2080
|
+
* // In your Cloudflare Worker
|
|
2081
|
+
* alepha.set(cloudflareD1Options, { binding: env.DB });
|
|
2082
|
+
* ```
|
|
2083
|
+
*/
|
|
2084
|
+
var CloudflareD1Provider = class extends DatabaseProvider {
|
|
2085
|
+
kit = $inject(DrizzleKitProvider);
|
|
2086
|
+
log = $logger();
|
|
2087
|
+
builder = $inject(SqliteModelBuilder);
|
|
2088
|
+
env = $env(t.object({ DATABASE_URL: t.string({ description: "Expect to be 'cloudflare-d1://name:id'" }) }));
|
|
2089
|
+
d1;
|
|
2090
|
+
drizzleDb;
|
|
2091
|
+
get name() {
|
|
2092
|
+
return "d1";
|
|
2093
|
+
}
|
|
2094
|
+
dialect = "sqlite";
|
|
2095
|
+
get url() {
|
|
2096
|
+
return this.env.DATABASE_URL;
|
|
2097
|
+
}
|
|
2098
|
+
get db() {
|
|
2099
|
+
if (!this.drizzleDb) throw new AlephaError("D1 database not initialized");
|
|
2100
|
+
return this.drizzleDb;
|
|
2101
|
+
}
|
|
2102
|
+
async execute(query) {
|
|
2103
|
+
const { rows } = await this.db.run(query);
|
|
2104
|
+
return rows;
|
|
2105
|
+
}
|
|
2106
|
+
onStart = $hook({
|
|
2107
|
+
on: "start",
|
|
2108
|
+
handler: async () => {
|
|
2109
|
+
const [bindingName] = this.env.DATABASE_URL.replace("cloudflare-d1://", "").split(":");
|
|
2110
|
+
const cloudflareEnv = this.alepha.store.get("cloudflare.env");
|
|
2111
|
+
if (!cloudflareEnv) throw new AlephaError("Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.");
|
|
2112
|
+
const binding = cloudflareEnv[bindingName];
|
|
2113
|
+
if (!binding) throw new AlephaError(`D1 binding '${bindingName}' not found in Cloudflare Workers environment.`);
|
|
2114
|
+
this.d1 = binding;
|
|
2115
|
+
const { drizzle: drizzle$3 } = await import("drizzle-orm/d1");
|
|
2116
|
+
this.drizzleDb = drizzle$3(this.d1);
|
|
2117
|
+
await this.migrateDatabase();
|
|
2118
|
+
this.log.info("Using Cloudflare D1 database");
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
async executeMigrations(migrationsFolder) {
|
|
2122
|
+
const { migrate: migrate$3 } = await import("drizzle-orm/d1/migrator");
|
|
2123
|
+
await migrate$3(this.db, { migrationsFolder });
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* Override development migration to skip sync (not supported on D1).
|
|
2127
|
+
* D1 requires proper migrations to be applied.
|
|
2128
|
+
*/
|
|
2129
|
+
async runDevelopmentMigration(migrationsFolder) {
|
|
2130
|
+
await this.executeMigrations(migrationsFolder);
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Override test migration to run migrations instead of sync.
|
|
2134
|
+
* D1 doesn't support schema synchronization.
|
|
2135
|
+
*/
|
|
2136
|
+
async runTestMigration() {
|
|
2137
|
+
const migrationsFolder = this.getMigrationsFolder();
|
|
2138
|
+
try {
|
|
2139
|
+
await this.executeMigrations(migrationsFolder);
|
|
2140
|
+
} catch {
|
|
2141
|
+
this.log.warn("D1 migrations failed in test environment - ensure migrations exist");
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2161
2144
|
};
|
|
2162
2145
|
|
|
2163
2146
|
//#endregion
|
|
@@ -2283,16 +2266,6 @@ var NodePostgresProvider = class NodePostgresProvider extends DatabaseProvider {
|
|
|
2283
2266
|
const mode = url.searchParams.get("sslmode");
|
|
2284
2267
|
for (const it of NodePostgresProvider.SSL_MODES) if (mode === it) return it;
|
|
2285
2268
|
}
|
|
2286
|
-
/**
|
|
2287
|
-
* For testing purposes, generate a unique schema name.
|
|
2288
|
-
* The schema name will be generated based on the current date and time.
|
|
2289
|
-
* It will be in the format of `test_YYYYMMDD_HHMMSS_randomSuffix`.
|
|
2290
|
-
*/
|
|
2291
|
-
generateTestSchemaName() {
|
|
2292
|
-
const pad = (n) => n.toString().padStart(2, "0");
|
|
2293
|
-
const now = /* @__PURE__ */ new Date();
|
|
2294
|
-
return `test_${`${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}_${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(now.getUTCSeconds())}`}_${Math.random().toString(36).slice(2, 6)}`;
|
|
2295
|
-
}
|
|
2296
2269
|
};
|
|
2297
2270
|
|
|
2298
2271
|
//#endregion
|
|
@@ -2788,8 +2761,8 @@ const $transaction = (opts) => {
|
|
|
2788
2761
|
};
|
|
2789
2762
|
|
|
2790
2763
|
//#endregion
|
|
2791
|
-
//#region ../../src/orm/providers/
|
|
2792
|
-
var
|
|
2764
|
+
//#region ../../src/orm/providers/DatabaseTypeProvider.ts
|
|
2765
|
+
var DatabaseTypeProvider = class {
|
|
2793
2766
|
attr = pgAttr;
|
|
2794
2767
|
/**
|
|
2795
2768
|
* Creates a primary key with an identity column.
|
|
@@ -2872,13 +2845,34 @@ var PostgresTypeProvider = class {
|
|
|
2872
2845
|
};
|
|
2873
2846
|
/**
|
|
2874
2847
|
* Creates a page schema for a given object schema.
|
|
2875
|
-
* It's used by {@link
|
|
2848
|
+
* It's used by {@link Repository#paginate} method.
|
|
2876
2849
|
*/
|
|
2877
2850
|
page = (resource, options) => {
|
|
2878
2851
|
return pageSchema$1(resource, options);
|
|
2879
2852
|
};
|
|
2880
2853
|
};
|
|
2881
|
-
|
|
2854
|
+
/**
|
|
2855
|
+
* Wrapper of TypeProvider (`t`) for database types.
|
|
2856
|
+
*
|
|
2857
|
+
* Use `db` for improve TypeBox schema definitions with database-specific attributes.
|
|
2858
|
+
*
|
|
2859
|
+
* @example
|
|
2860
|
+
* ```ts
|
|
2861
|
+
* import { t } from "alepha";
|
|
2862
|
+
* import { db } from "alepha/orm";
|
|
2863
|
+
*
|
|
2864
|
+
* const userSchema = t.object({
|
|
2865
|
+
* id: db.primaryKey(t.uuid()),
|
|
2866
|
+
* email: t.email(),
|
|
2867
|
+
* createdAt: db.createdAt(),
|
|
2868
|
+
* });
|
|
2869
|
+
* ```
|
|
2870
|
+
*/
|
|
2871
|
+
const db = new DatabaseTypeProvider();
|
|
2872
|
+
/**
|
|
2873
|
+
* @deprecated Use `db` instead.
|
|
2874
|
+
*/
|
|
2875
|
+
const pg = db;
|
|
2882
2876
|
|
|
2883
2877
|
//#endregion
|
|
2884
2878
|
//#region ../../src/orm/schemas/legacyIdSchema.ts
|
|
@@ -2893,21 +2887,25 @@ const legacyIdSchema = pgAttr(pgAttr(pgAttr(t.integer(), PG_PRIMARY_KEY), PG_SER
|
|
|
2893
2887
|
* Postgres client based on Drizzle ORM, Alepha type-safe friendly.
|
|
2894
2888
|
*
|
|
2895
2889
|
* ```ts
|
|
2890
|
+
* import { t } from "alepha";
|
|
2891
|
+
* import { $entity, $repository, db } from "alepha/postgres";
|
|
2892
|
+
*
|
|
2896
2893
|
* const users = $entity({
|
|
2897
2894
|
* name: "users",
|
|
2898
2895
|
* schema: t.object({
|
|
2899
|
-
* id:
|
|
2896
|
+
* id: db.primaryKey(),
|
|
2900
2897
|
* name: t.text(),
|
|
2901
2898
|
* email: t.text(),
|
|
2902
2899
|
* }),
|
|
2903
2900
|
* });
|
|
2904
2901
|
*
|
|
2905
|
-
* class
|
|
2902
|
+
* class App {
|
|
2906
2903
|
* users = $repository(users);
|
|
2907
|
-
* }
|
|
2908
2904
|
*
|
|
2909
|
-
*
|
|
2910
|
-
*
|
|
2905
|
+
* getUserByName(name: string) {
|
|
2906
|
+
* return this.users.findOne({ name: { eq: name } });
|
|
2907
|
+
* }
|
|
2908
|
+
* }
|
|
2911
2909
|
* ```
|
|
2912
2910
|
*
|
|
2913
2911
|
* This is not a full ORM, but rather a set of tools to work with Postgres databases in a type-safe way.
|
|
@@ -2937,6 +2935,8 @@ const AlephaPostgres = $module({
|
|
|
2937
2935
|
NodePostgresProvider,
|
|
2938
2936
|
PglitePostgresProvider,
|
|
2939
2937
|
NodeSqliteProvider,
|
|
2938
|
+
BunPostgresProvider,
|
|
2939
|
+
BunSqliteProvider,
|
|
2940
2940
|
CloudflareD1Provider,
|
|
2941
2941
|
SqliteModelBuilder,
|
|
2942
2942
|
PostgresModelBuilder,
|
|
@@ -2944,7 +2944,6 @@ const AlephaPostgres = $module({
|
|
|
2944
2944
|
RepositoryProvider,
|
|
2945
2945
|
Repository,
|
|
2946
2946
|
PgRelationManager,
|
|
2947
|
-
PgJsonQueryManager,
|
|
2948
2947
|
QueryManager
|
|
2949
2948
|
],
|
|
2950
2949
|
register: (alepha) => {
|
|
@@ -2977,18 +2976,18 @@ const AlephaPostgres = $module({
|
|
|
2977
2976
|
alepha.with({
|
|
2978
2977
|
optional: true,
|
|
2979
2978
|
provide: DatabaseProvider,
|
|
2980
|
-
use: NodePostgresProvider
|
|
2979
|
+
use: alepha.isBun() ? BunPostgresProvider : NodePostgresProvider
|
|
2981
2980
|
});
|
|
2982
2981
|
return;
|
|
2983
2982
|
}
|
|
2984
2983
|
alepha.with({
|
|
2985
2984
|
optional: true,
|
|
2986
2985
|
provide: DatabaseProvider,
|
|
2987
|
-
use: NodeSqliteProvider
|
|
2986
|
+
use: alepha.isBun() ? BunSqliteProvider : NodeSqliteProvider
|
|
2988
2987
|
});
|
|
2989
2988
|
}
|
|
2990
2989
|
});
|
|
2991
2990
|
|
|
2992
2991
|
//#endregion
|
|
2993
|
-
export { $entity, $repository, $sequence, $transaction, AlephaPostgres, CloudflareD1Provider, DatabaseProvider, DbConflictError, DbEntityNotFoundError, DbError, DbMigrationError, DbVersionMismatchError, DrizzleKitProvider, EntityPrimitive, NodePostgresProvider, NodeSqliteProvider, PG_CREATED_AT, PG_DEFAULT, PG_DELETED_AT, PG_ENUM, PG_IDENTITY, PG_PRIMARY_KEY, PG_REF, PG_SERIAL, PG_UPDATED_AT, PG_VERSION,
|
|
2992
|
+
export { $entity, $repository, $sequence, $transaction, AlephaPostgres, BunPostgresProvider, BunSqliteProvider, CloudflareD1Provider, DatabaseProvider, DatabaseTypeProvider, DbConflictError, DbEntityNotFoundError, DbError, DbMigrationError, DbVersionMismatchError, DrizzleKitProvider, EntityPrimitive, NodePostgresProvider, NodeSqliteProvider, PG_CREATED_AT, PG_DEFAULT, PG_DELETED_AT, PG_ENUM, PG_IDENTITY, PG_PRIMARY_KEY, PG_REF, PG_SERIAL, PG_UPDATED_AT, PG_VERSION, Repository, RepositoryProvider, SequencePrimitive, buildQueryString, bunSqliteOptions, db, drizzle, getAttrFields, insertSchema, legacyIdSchema, nodeSqliteOptions, pageQuerySchema, pageSchema, parseQueryString, pg, pgAttr, schema, sql, updateSchema };
|
|
2994
2993
|
//# sourceMappingURL=index.js.map
|