@stacksjs/database 0.70.87 → 0.70.90
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/auth-tables.js +220 -0
- package/dist/class-seeder.js +116 -0
- package/dist/column.d.ts +17 -0
- package/dist/column.js +26 -0
- package/dist/custom/audits.js +57 -0
- package/dist/custom/errors.js +48 -0
- package/dist/custom/index.js +3 -0
- package/dist/custom/jobs.js +449 -0
- package/dist/database.js +178 -0
- package/dist/defaults.js +48 -0
- package/dist/driver-config.js +144 -0
- package/dist/drivers/defaults/index.js +2 -0
- package/dist/drivers/defaults/passwords.js +106 -0
- package/dist/drivers/defaults/traits.js +1125 -0
- package/dist/drivers/dynamodb.js +607 -0
- package/dist/drivers/helpers.js +206 -0
- package/dist/drivers/index.js +9 -0
- package/dist/drivers/mysql.js +322 -0
- package/dist/drivers/postgres.js +411 -0
- package/dist/drivers/sqlite.js +397 -0
- package/dist/factory.js +51 -0
- package/dist/fk-audit.js +181 -0
- package/dist/index.js +55 -1263
- package/dist/migration-lock.js +143 -0
- package/dist/migrations.js +528 -0
- package/dist/notification-tables.js +54 -0
- package/dist/query-logger.js +213 -0
- package/dist/query-parser.js +93 -0
- package/dist/rbac-tables.js +84 -0
- package/dist/safe-migrations.js +59 -0
- package/dist/schema.d.ts +4 -0
- package/dist/schema.js +10 -0
- package/dist/seed-scaffold.js +144 -0
- package/dist/seeder.js +363 -0
- package/dist/sql-helpers.js +24 -0
- package/dist/table.d.ts +7 -0
- package/dist/table.js +26 -0
- package/dist/tools/setup.d.ts +1 -0
- package/dist/tools/setup.js +6 -0
- package/dist/transaction-context.js +62 -0
- package/dist/types.js +23 -0
- package/dist/unique-audit.js +174 -0
- package/dist/utils.js +163 -0
- package/dist/uuid-columns.js +68 -0
- package/dist/validators.js +122 -0
- package/package.json +11 -11
package/dist/utils.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createQueryBuilder, setConfig } from "@stacksjs/query-builder";
|
|
3
|
+
import { env as envVars } from "@stacksjs/env";
|
|
4
|
+
import { getConnectionDefaults } from "./defaults";
|
|
5
|
+
const sqliteDefaults = getConnectionDefaults("sqlite", envVars), mysqlDefaults = getConnectionDefaults("mysql", envVars), postgresDefaults = getConnectionDefaults("postgres", envVars);
|
|
6
|
+
let appEnv = envVars.APP_ENV || "local", dbDriver = envVars.DB_CONNECTION || "sqlite", dbConfig = {
|
|
7
|
+
connections: {
|
|
8
|
+
sqlite: { database: sqliteDefaults.database, prefix: "" },
|
|
9
|
+
mysql: { name: mysqlDefaults.database, host: mysqlDefaults.host, username: mysqlDefaults.username, password: mysqlDefaults.password, port: mysqlDefaults.port, prefix: "" },
|
|
10
|
+
singlestore: { name: mysqlDefaults.database, host: mysqlDefaults.host, username: mysqlDefaults.username, password: mysqlDefaults.password, port: mysqlDefaults.port, prefix: "" },
|
|
11
|
+
postgres: { name: postgresDefaults.database, host: postgresDefaults.host, username: postgresDefaults.username, password: postgresDefaults.password, port: postgresDefaults.port, prefix: "" }
|
|
12
|
+
}
|
|
13
|
+
}, dbConfigLockTail = Promise.resolve();
|
|
14
|
+
export function acquireDbConfigLock() {
|
|
15
|
+
let release = () => {};
|
|
16
|
+
const held = new Promise((resolve) => {
|
|
17
|
+
release = resolve;
|
|
18
|
+
}), acquired = dbConfigLockTail.then(() => release);
|
|
19
|
+
dbConfigLockTail = dbConfigLockTail.then(() => held);
|
|
20
|
+
return acquired;
|
|
21
|
+
}
|
|
22
|
+
export function initializeDbConfig(config) {
|
|
23
|
+
if (config?.app?.env)
|
|
24
|
+
appEnv = config.app.env;
|
|
25
|
+
if (config?.database?.default)
|
|
26
|
+
dbDriver = config.database.default;
|
|
27
|
+
if (config?.database)
|
|
28
|
+
dbConfig = config.database;
|
|
29
|
+
updateQueryBuilderConfig();
|
|
30
|
+
_dbInstance = null;
|
|
31
|
+
}
|
|
32
|
+
function getEnv() {
|
|
33
|
+
return appEnv;
|
|
34
|
+
}
|
|
35
|
+
function getDriver() {
|
|
36
|
+
return dbDriver;
|
|
37
|
+
}
|
|
38
|
+
function getDatabaseConfig() {
|
|
39
|
+
return dbConfig;
|
|
40
|
+
}
|
|
41
|
+
function getDialect() {
|
|
42
|
+
const driver = getDriver();
|
|
43
|
+
if (driver === "sqlite")
|
|
44
|
+
return "sqlite";
|
|
45
|
+
if (driver === "mysql")
|
|
46
|
+
return "mysql";
|
|
47
|
+
if (driver === "singlestore")
|
|
48
|
+
return "singlestore";
|
|
49
|
+
if (driver === "postgres")
|
|
50
|
+
return "postgres";
|
|
51
|
+
return "sqlite";
|
|
52
|
+
}
|
|
53
|
+
function getDbConfig() {
|
|
54
|
+
const driver = getDriver(), database = getDatabaseConfig(), env = getEnv();
|
|
55
|
+
if (driver === "sqlite") {
|
|
56
|
+
const defaultName = env !== "testing" ? "database/stacks.sqlite" : "database/stacks_testing.sqlite";
|
|
57
|
+
return {
|
|
58
|
+
database: database.connections?.sqlite?.database ?? defaultName
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (driver === "mysql")
|
|
62
|
+
return {
|
|
63
|
+
database: database.connections?.mysql?.name || "stacks",
|
|
64
|
+
host: database.connections?.mysql?.host ?? "127.0.0.1",
|
|
65
|
+
username: database.connections?.mysql?.username ?? "root",
|
|
66
|
+
password: database.connections?.mysql?.password ?? "",
|
|
67
|
+
port: database.connections?.mysql?.port ?? 3306
|
|
68
|
+
};
|
|
69
|
+
if (driver === "singlestore")
|
|
70
|
+
return {
|
|
71
|
+
database: database.connections?.singlestore?.name || "stacks",
|
|
72
|
+
host: database.connections?.singlestore?.host ?? "127.0.0.1",
|
|
73
|
+
username: database.connections?.singlestore?.username ?? "root",
|
|
74
|
+
password: database.connections?.singlestore?.password ?? "",
|
|
75
|
+
port: database.connections?.singlestore?.port ?? 3306
|
|
76
|
+
};
|
|
77
|
+
if (driver === "postgres") {
|
|
78
|
+
const dbName = database.connections?.postgres?.name ?? "stacks";
|
|
79
|
+
return {
|
|
80
|
+
database: env === "testing" ? `${dbName}_testing` : dbName,
|
|
81
|
+
host: database.connections?.postgres?.host ?? "127.0.0.1",
|
|
82
|
+
username: database.connections?.postgres?.username ?? "",
|
|
83
|
+
password: database.connections?.postgres?.password ?? "",
|
|
84
|
+
port: database.connections?.postgres?.port ?? 5432
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { database: ":memory:" };
|
|
88
|
+
}
|
|
89
|
+
function updateQueryBuilderConfig() {
|
|
90
|
+
const dialect = getDialect(), dbConfigForQb = getDbConfig();
|
|
91
|
+
setConfig({
|
|
92
|
+
dialect,
|
|
93
|
+
database: dbConfigForQb,
|
|
94
|
+
verbose: getEnv() !== "production",
|
|
95
|
+
timestamps: {
|
|
96
|
+
createdAt: "created_at",
|
|
97
|
+
updatedAt: "updated_at",
|
|
98
|
+
defaultOrderColumn: "created_at"
|
|
99
|
+
},
|
|
100
|
+
softDeletes: {
|
|
101
|
+
enabled: !0,
|
|
102
|
+
column: "deleted_at",
|
|
103
|
+
defaultFilter: !0
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
updateQueryBuilderConfig();
|
|
108
|
+
let _dbInstance = null, _configInitPromise = null;
|
|
109
|
+
function ensureConfigLoaded() {
|
|
110
|
+
if (!_configInitPromise)
|
|
111
|
+
_configInitPromise = (async () => {
|
|
112
|
+
try {
|
|
113
|
+
const { config, overridesReady } = await import("@stacksjs/config");
|
|
114
|
+
await overridesReady;
|
|
115
|
+
if (config) {
|
|
116
|
+
initializeDbConfig(config);
|
|
117
|
+
_dbInstance = null;
|
|
118
|
+
}
|
|
119
|
+
} catch {}
|
|
120
|
+
})();
|
|
121
|
+
return _configInitPromise;
|
|
122
|
+
}
|
|
123
|
+
export async function ensureDatabaseConfigLoaded() {
|
|
124
|
+
await ensureConfigLoaded();
|
|
125
|
+
}
|
|
126
|
+
export { applySqlitePragmas, SQLITE_BOOTSTRAP_PRAGMAS } from "@stacksjs/query-builder";
|
|
127
|
+
const sqliteTxOwner = new AsyncLocalStorage;
|
|
128
|
+
let sqliteTxTail = Promise.resolve();
|
|
129
|
+
function serializeSqliteTransaction(run) {
|
|
130
|
+
if (sqliteTxOwner.getStore())
|
|
131
|
+
return run();
|
|
132
|
+
const result = sqliteTxTail.then(() => sqliteTxOwner.run(!0, run));
|
|
133
|
+
sqliteTxTail = result.then(() => {
|
|
134
|
+
return;
|
|
135
|
+
}, () => {
|
|
136
|
+
return;
|
|
137
|
+
});
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
function applySqliteTransactionSerialization(instance) {
|
|
141
|
+
const original = instance.transaction.bind(instance);
|
|
142
|
+
instance.transaction = (...args) => serializeSqliteTransaction(() => original(...args));
|
|
143
|
+
}
|
|
144
|
+
function getDb() {
|
|
145
|
+
if (!_dbInstance) {
|
|
146
|
+
updateQueryBuilderConfig();
|
|
147
|
+
_dbInstance = createQueryBuilder();
|
|
148
|
+
if (getDialect() === "sqlite")
|
|
149
|
+
applySqliteTransactionSerialization(_dbInstance);
|
|
150
|
+
}
|
|
151
|
+
return _dbInstance;
|
|
152
|
+
}
|
|
153
|
+
ensureConfigLoaded();
|
|
154
|
+
export const db = new Proxy({}, {
|
|
155
|
+
get(_target, prop) {
|
|
156
|
+
const instance = getDb(), value = instance[prop];
|
|
157
|
+
if (typeof value === "function")
|
|
158
|
+
return value.bind(instance);
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
export { setConfig };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { log } from "@stacksjs/logging";
|
|
3
|
+
import { path } from "@stacksjs/path";
|
|
4
|
+
import { getModelName, getTableName } from "@stacksjs/orm";
|
|
5
|
+
import { fs } from "@stacksjs/storage";
|
|
6
|
+
import { db } from "./utils";
|
|
7
|
+
import { sqlHelpers } from "./sql-helpers";
|
|
8
|
+
function getDbDriver() {
|
|
9
|
+
return process.env.DB_CONNECTION || "sqlite";
|
|
10
|
+
}
|
|
11
|
+
function uuidColumnType(sql) {
|
|
12
|
+
if (sql.isPostgres)
|
|
13
|
+
return "UUID";
|
|
14
|
+
if (sql.isMysql)
|
|
15
|
+
return "VARCHAR(255)";
|
|
16
|
+
return "TEXT";
|
|
17
|
+
}
|
|
18
|
+
export function uuidColumnSql(table, sql) {
|
|
19
|
+
return `ALTER TABLE ${table} ADD COLUMN uuid ${uuidColumnType(sql)}`;
|
|
20
|
+
}
|
|
21
|
+
async function loadModelsFrom(dir) {
|
|
22
|
+
const out = [];
|
|
23
|
+
if (!fs.existsSync(dir))
|
|
24
|
+
return out;
|
|
25
|
+
const entries = fs.readdirSync(dir, { withFileTypes: !0 });
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
const fullPath = path.join(dir, entry.name);
|
|
28
|
+
if (entry.isDirectory()) {
|
|
29
|
+
out.push(...await loadModelsFrom(fullPath));
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!entry.name.endsWith(".ts"))
|
|
33
|
+
continue;
|
|
34
|
+
if (entry.name.startsWith("_") || entry.name.startsWith("index"))
|
|
35
|
+
continue;
|
|
36
|
+
try {
|
|
37
|
+
const imported = (await import(fullPath)).default;
|
|
38
|
+
if (imported?.name || imported?.table)
|
|
39
|
+
out.push({ filePath: fullPath, model: imported });
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
export async function findUuidTables() {
|
|
45
|
+
const dirs = [path.userModelsPath(), path.frameworkPath("defaults/app/Models")], tables = new Set;
|
|
46
|
+
for (const dir of dirs)
|
|
47
|
+
for (const { filePath, model } of await loadModelsFrom(dir)) {
|
|
48
|
+
if (!model.traits?.useUuid)
|
|
49
|
+
continue;
|
|
50
|
+
tables.add(getTableName(model, filePath));
|
|
51
|
+
}
|
|
52
|
+
return [...tables];
|
|
53
|
+
}
|
|
54
|
+
export async function ensureUuidColumns(sql, options = {}) {
|
|
55
|
+
const tables = await findUuidTables();
|
|
56
|
+
for (const table of tables)
|
|
57
|
+
try {
|
|
58
|
+
await db.unsafe(uuidColumnSql(table, sql)).execute();
|
|
59
|
+
if (options.verbose)
|
|
60
|
+
log.debug(`[uuid-columns] Added uuid column to ${table}`);
|
|
61
|
+
} catch {
|
|
62
|
+
if (options.verbose)
|
|
63
|
+
log.debug(`[uuid-columns] Skipped (already applied or ${table} missing): uuid column`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function ensureUuidColumnsForCurrentDriver(options = {}) {
|
|
67
|
+
await ensureUuidColumns(sqlHelpers(getDbDriver()), options);
|
|
68
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export function isStringValidator(v) {
|
|
2
|
+
return v.name === "string";
|
|
3
|
+
}
|
|
4
|
+
export function isNumberValidator(v) {
|
|
5
|
+
return v.name === "number";
|
|
6
|
+
}
|
|
7
|
+
export function enumValidator(v) {
|
|
8
|
+
return v.name === "enum";
|
|
9
|
+
}
|
|
10
|
+
export function isBooleanValidator(v) {
|
|
11
|
+
return v.name === "boolean";
|
|
12
|
+
}
|
|
13
|
+
export function isDateValidator(v) {
|
|
14
|
+
return v.name === "date";
|
|
15
|
+
}
|
|
16
|
+
export function isUnixValidator(v) {
|
|
17
|
+
return v.name === "unix";
|
|
18
|
+
}
|
|
19
|
+
export function isFloatValidator(v) {
|
|
20
|
+
return v.name === "float";
|
|
21
|
+
}
|
|
22
|
+
export function isDatetimeValidator(v) {
|
|
23
|
+
return v.name === "datetime";
|
|
24
|
+
}
|
|
25
|
+
export function isTimestampValidator(v) {
|
|
26
|
+
return v.name === "timestamp";
|
|
27
|
+
}
|
|
28
|
+
export function isTimestampTzValidator(v) {
|
|
29
|
+
return v.name === "timestampTz";
|
|
30
|
+
}
|
|
31
|
+
export function isDecimalValidator(v) {
|
|
32
|
+
return v.name === "decimal";
|
|
33
|
+
}
|
|
34
|
+
export function isSmallintValidator(v) {
|
|
35
|
+
return v.name === "smallint";
|
|
36
|
+
}
|
|
37
|
+
export function isIntegerValidator(v) {
|
|
38
|
+
return v.name === "integer";
|
|
39
|
+
}
|
|
40
|
+
export function isBigintValidator(v) {
|
|
41
|
+
return v.name === "bigint";
|
|
42
|
+
}
|
|
43
|
+
export function isBinaryValidator(v) {
|
|
44
|
+
return v.name === "binary";
|
|
45
|
+
}
|
|
46
|
+
export function isBlobValidator(v) {
|
|
47
|
+
return v.name === "blob";
|
|
48
|
+
}
|
|
49
|
+
export function isJsonValidator(v) {
|
|
50
|
+
return v.name === "json";
|
|
51
|
+
}
|
|
52
|
+
export function checkValidator(validator, driver) {
|
|
53
|
+
if (enumValidator(validator))
|
|
54
|
+
return prepareEnumColumnType(validator, driver);
|
|
55
|
+
if (isStringValidator(validator))
|
|
56
|
+
return prepareTextColumnType(validator, driver);
|
|
57
|
+
if (isNumberValidator(validator))
|
|
58
|
+
return prepareNumberColumnType(validator, driver);
|
|
59
|
+
if (isBooleanValidator(validator))
|
|
60
|
+
return "'boolean'";
|
|
61
|
+
if (isDateValidator(validator))
|
|
62
|
+
return "'date'";
|
|
63
|
+
if (isDatetimeValidator(validator))
|
|
64
|
+
return "'datetime'";
|
|
65
|
+
if (isUnixValidator(validator))
|
|
66
|
+
return "'bigint'";
|
|
67
|
+
if (isTimestampValidator(validator))
|
|
68
|
+
return "'timestamp'";
|
|
69
|
+
if (isTimestampTzValidator(validator))
|
|
70
|
+
return "'timestamp'";
|
|
71
|
+
if (isFloatValidator(validator))
|
|
72
|
+
return "'float'";
|
|
73
|
+
if (isSmallintValidator(validator))
|
|
74
|
+
return "'smallint'";
|
|
75
|
+
if (isDecimalValidator(validator))
|
|
76
|
+
return "'decimal'";
|
|
77
|
+
if (isIntegerValidator(validator))
|
|
78
|
+
return "'integer'";
|
|
79
|
+
if (isBigintValidator(validator))
|
|
80
|
+
return "'bigint'";
|
|
81
|
+
if (isBinaryValidator(validator))
|
|
82
|
+
return "'binary'";
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
export function prepareNumberColumnType(validator, driver = "mysql") {
|
|
86
|
+
if (driver === "sqlite")
|
|
87
|
+
return "'integer'";
|
|
88
|
+
if ("getRules" in validator) {
|
|
89
|
+
const minRule = validator.getRules().find((rule) => rule.name === "min"), maxRule = validator.getRules().find((rule) => rule.name === "max"), min = minRule?.params?.min ?? -2147483648, max = maxRule?.params?.max ?? 2147483647;
|
|
90
|
+
return min >= -2147483648 && max <= 2147483647 ? "'integer'" : "'bigint'";
|
|
91
|
+
}
|
|
92
|
+
return "'integer'";
|
|
93
|
+
}
|
|
94
|
+
export function prepareEnumColumnType(validator, driver = "mysql") {
|
|
95
|
+
const allowedValues = validator.getAllowedValues();
|
|
96
|
+
if (!allowedValues)
|
|
97
|
+
throw Error("Enum rule found but no allowedValues defined");
|
|
98
|
+
const enumStructure = allowedValues.map((value) => `'${value}'`).join(", ");
|
|
99
|
+
if (driver === "sqlite")
|
|
100
|
+
return "'text'";
|
|
101
|
+
return `sql\`enum(${enumStructure})\``;
|
|
102
|
+
}
|
|
103
|
+
export function prepareTextColumnType(validator, driver = "mysql") {
|
|
104
|
+
if (driver === "sqlite")
|
|
105
|
+
return "'text'";
|
|
106
|
+
return `'varchar(${findCharacterLength(validator)})'`;
|
|
107
|
+
}
|
|
108
|
+
export function prepareDateTimeColumnType(validator, driver = "mysql") {
|
|
109
|
+
if (driver === "sqlite")
|
|
110
|
+
return "'text'";
|
|
111
|
+
const name = validator.name;
|
|
112
|
+
if (name === "unix")
|
|
113
|
+
return "'bigint'";
|
|
114
|
+
return name || "date";
|
|
115
|
+
}
|
|
116
|
+
export function findCharacterLength(validator) {
|
|
117
|
+
if ("getRules" in validator) {
|
|
118
|
+
const maxLengthRule = validator.getRules().find((rule) => rule.name === "max");
|
|
119
|
+
return maxLengthRule?.params?.length || maxLengthRule?.params?.max || 255;
|
|
120
|
+
}
|
|
121
|
+
return 255;
|
|
122
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/database",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.90",
|
|
6
6
|
"description": "The Stacks database integration.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -55,18 +55,18 @@
|
|
|
55
55
|
"prepublishOnly": "bun run build"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"bun-query-builder": "^0.1.
|
|
58
|
+
"bun-query-builder": "^0.1.50"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@stacksjs/cli": "0.70.
|
|
62
|
-
"@stacksjs/config": "0.70.
|
|
63
|
-
"@stacksjs/logging": "0.70.
|
|
64
|
-
"@stacksjs/router": "0.70.
|
|
61
|
+
"@stacksjs/cli": "0.70.90",
|
|
62
|
+
"@stacksjs/config": "0.70.90",
|
|
63
|
+
"@stacksjs/logging": "0.70.90",
|
|
64
|
+
"@stacksjs/router": "0.70.90",
|
|
65
65
|
"better-dx": "^0.2.16",
|
|
66
|
-
"@stacksjs/path": "0.70.
|
|
67
|
-
"@stacksjs/query-builder": "0.70.
|
|
68
|
-
"@stacksjs/storage": "0.70.
|
|
69
|
-
"@stacksjs/strings": "0.70.
|
|
70
|
-
"@stacksjs/utils": "0.70.
|
|
66
|
+
"@stacksjs/path": "0.70.90",
|
|
67
|
+
"@stacksjs/query-builder": "0.70.90",
|
|
68
|
+
"@stacksjs/storage": "0.70.90",
|
|
69
|
+
"@stacksjs/strings": "0.70.90",
|
|
70
|
+
"@stacksjs/utils": "0.70.90"
|
|
71
71
|
}
|
|
72
72
|
}
|