@stacksjs/database 0.70.256 → 0.70.258
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 +12 -12
- package/dist/database.js +2 -1
- package/dist/datetime-columns.d.ts +36 -0
- package/dist/datetime-columns.js +85 -0
- package/dist/ddl-constraints.d.ts +31 -0
- package/dist/ddl-constraints.js +111 -0
- package/dist/dialect.d.ts +82 -0
- package/dist/dialect.js +79 -0
- package/dist/driver-config.d.ts +55 -5
- package/dist/driver-config.js +28 -0
- package/dist/drivers/defaults/index.d.ts +0 -1
- package/dist/drivers/defaults/index.js +0 -1
- package/dist/drivers/defaults/traits.d.ts +17 -31
- package/dist/drivers/defaults/traits.js +18 -1142
- package/dist/drivers/mysql.d.ts +0 -1
- package/dist/drivers/mysql.js +1 -14
- package/dist/drivers/postgres.d.ts +0 -1
- package/dist/drivers/postgres.js +2 -35
- package/dist/index.d.ts +20 -0
- package/dist/index.js +6 -0
- package/dist/notification-tables.js +6 -6
- package/dist/query-logger.js +2 -1
- package/dist/rbac-tables.d.ts +3 -3
- package/dist/rbac-tables.js +16 -13
- package/dist/replicas.d.ts +52 -0
- package/dist/replicas.js +74 -0
- package/dist/schema.d.ts +1 -0
- package/dist/sql-helpers.d.ts +60 -7
- package/dist/sql-helpers.js +31 -5
- package/dist/trait-tables.d.ts +126 -0
- package/dist/trait-tables.js +206 -0
- package/dist/utils.d.ts +33 -0
- package/dist/utils.js +102 -13
- package/dist/vschema.d.ts +84 -0
- package/dist/vschema.js +121 -0
- package/package.json +11 -11
- package/dist/drivers/defaults/passwords.d.ts +0 -4
- package/dist/drivers/defaults/passwords.js +0 -106
package/dist/vschema.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export function foreignKeyForModel(modelName) {
|
|
2
|
+
return `${modelName.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toLowerCase()}_id`;
|
|
3
|
+
}
|
|
4
|
+
export function decideSharding(model, tableByModel) {
|
|
5
|
+
const declared = model.sharding;
|
|
6
|
+
if (declared?.unsharded)
|
|
7
|
+
return {
|
|
8
|
+
table: model.table,
|
|
9
|
+
column: null,
|
|
10
|
+
vindex: null,
|
|
11
|
+
reason: "reference table"
|
|
12
|
+
};
|
|
13
|
+
if (declared?.column)
|
|
14
|
+
return {
|
|
15
|
+
table: model.table,
|
|
16
|
+
column: declared.column,
|
|
17
|
+
vindex: declared.vindex ?? "hash",
|
|
18
|
+
reason: "explicit"
|
|
19
|
+
};
|
|
20
|
+
const parentModel = model.belongsTo[0];
|
|
21
|
+
if (parentModel) {
|
|
22
|
+
const parentTable = tableByModel.get(parentModel);
|
|
23
|
+
return {
|
|
24
|
+
table: model.table,
|
|
25
|
+
column: foreignKeyForModel(parentModel),
|
|
26
|
+
vindex: declared?.vindex ?? "hash",
|
|
27
|
+
reason: "co-located with parent",
|
|
28
|
+
parent: parentTable ?? parentModel,
|
|
29
|
+
warning: model.belongsTo.length > 1 ? `belongs to ${model.belongsTo.length} parents; sharded by ${parentModel} only, so joins through ${model.belongsTo.slice(1).join(", ")} will scatter` : void 0
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
table: model.table,
|
|
34
|
+
column: "id",
|
|
35
|
+
vindex: declared?.vindex ?? "hash",
|
|
36
|
+
reason: "root entity"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function deriveVSchema(models) {
|
|
40
|
+
const tableByModel = new Map(models.map((m) => [m.name, m.table])), decisions = models.map((model) => decideSharding(model, tableByModel)), vindexes = {}, tables = {};
|
|
41
|
+
for (const [index, decision] of decisions.entries()) {
|
|
42
|
+
const model = models[index];
|
|
43
|
+
if (decision.reason === "reference table") {
|
|
44
|
+
tables[decision.table] = { type: "reference" };
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const vindexType = decision.vindex ?? "hash";
|
|
48
|
+
vindexes[vindexType] = { type: vindexType };
|
|
49
|
+
const table = {
|
|
50
|
+
column_vindexes: [{ column: decision.column, name: vindexType }]
|
|
51
|
+
};
|
|
52
|
+
if (!model.useUuid)
|
|
53
|
+
table.auto_increment = {
|
|
54
|
+
column: "id",
|
|
55
|
+
sequence: model.sharding?.sequence ?? `${model.table}_seq`
|
|
56
|
+
};
|
|
57
|
+
tables[decision.table] = table;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
vschema: { sharded: !0, vindexes, tables },
|
|
61
|
+
decisions
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function toShardableModel(definition, table) {
|
|
65
|
+
const raw = definition?.belongsTo;
|
|
66
|
+
let belongsTo = [];
|
|
67
|
+
if (typeof raw === "string")
|
|
68
|
+
belongsTo = [raw];
|
|
69
|
+
else if (Array.isArray(raw))
|
|
70
|
+
belongsTo = raw.map((entry) => typeof entry === "string" ? entry : entry?.model).filter(Boolean);
|
|
71
|
+
else if (raw && typeof raw === "object")
|
|
72
|
+
belongsTo = Object.keys(raw);
|
|
73
|
+
return {
|
|
74
|
+
name: definition?.name ?? table,
|
|
75
|
+
table,
|
|
76
|
+
belongsTo,
|
|
77
|
+
useUuid: Boolean(definition?.traits?.useUuid),
|
|
78
|
+
sharding: definition?.traits?.sharding
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function formatShardingReport(decisions) {
|
|
82
|
+
const lines = [], byReason = {
|
|
83
|
+
explicit: decisions.filter((d) => d.reason === "explicit"),
|
|
84
|
+
"co-located with parent": decisions.filter((d) => d.reason === "co-located with parent"),
|
|
85
|
+
"root entity": decisions.filter((d) => d.reason === "root entity"),
|
|
86
|
+
"reference table": decisions.filter((d) => d.reason === "reference table")
|
|
87
|
+
};
|
|
88
|
+
if (byReason["root entity"].length) {
|
|
89
|
+
lines.push("Root entities (sharded by their own id):");
|
|
90
|
+
for (const d of byReason["root entity"])
|
|
91
|
+
lines.push(` ${d.table} -> ${d.column} (${d.vindex})`);
|
|
92
|
+
lines.push("");
|
|
93
|
+
}
|
|
94
|
+
if (byReason["co-located with parent"].length) {
|
|
95
|
+
lines.push("Co-located with a parent (joins to that parent stay on one shard):");
|
|
96
|
+
for (const d of byReason["co-located with parent"])
|
|
97
|
+
lines.push(` ${d.table} -> ${d.column} (${d.vindex}), with ${d.parent}`);
|
|
98
|
+
lines.push("");
|
|
99
|
+
}
|
|
100
|
+
if (byReason.explicit.length) {
|
|
101
|
+
lines.push("Explicitly declared:");
|
|
102
|
+
for (const d of byReason.explicit)
|
|
103
|
+
lines.push(` ${d.table} -> ${d.column} (${d.vindex})`);
|
|
104
|
+
lines.push("");
|
|
105
|
+
}
|
|
106
|
+
if (byReason["reference table"].length) {
|
|
107
|
+
lines.push("Reference tables (copied to every shard):");
|
|
108
|
+
for (const d of byReason["reference table"])
|
|
109
|
+
lines.push(` ${d.table}`);
|
|
110
|
+
lines.push("");
|
|
111
|
+
}
|
|
112
|
+
const warnings = decisions.filter((d) => d.warning);
|
|
113
|
+
if (warnings.length) {
|
|
114
|
+
lines.push("Warnings:");
|
|
115
|
+
for (const d of warnings)
|
|
116
|
+
lines.push(` ${d.table}: ${d.warning}`);
|
|
117
|
+
lines.push("");
|
|
118
|
+
}
|
|
119
|
+
return lines.join(`
|
|
120
|
+
`);
|
|
121
|
+
}
|
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.258",
|
|
6
6
|
"description": "The Stacks database integration.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -54,19 +54,19 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@stacksjs/ts-validation": "^0.5.0",
|
|
57
|
-
"bun-query-builder": "^0.2.
|
|
57
|
+
"bun-query-builder": "^0.2.6",
|
|
58
58
|
"dynamodb-tooling": "^0.3.2"
|
|
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.258",
|
|
62
|
+
"@stacksjs/config": "0.70.258",
|
|
63
|
+
"@stacksjs/logging": "0.70.258",
|
|
64
|
+
"@stacksjs/router": "0.70.258",
|
|
65
65
|
"better-dx": "^0.2.17",
|
|
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.258",
|
|
67
|
+
"@stacksjs/query-builder": "0.70.258",
|
|
68
|
+
"@stacksjs/storage": "0.70.258",
|
|
69
|
+
"@stacksjs/strings": "0.70.258",
|
|
70
|
+
"@stacksjs/utils": "0.70.258"
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { log } from "@stacksjs/logging";
|
|
2
|
-
function italic(str) {
|
|
3
|
-
return `\x1B[3m${str}\x1B[23m`;
|
|
4
|
-
}
|
|
5
|
-
import { path } from "@stacksjs/path";
|
|
6
|
-
import { hasMigrationBeenCreated } from "../helpers";
|
|
7
|
-
export async function createPasswordResetsTable() {
|
|
8
|
-
if (await hasMigrationBeenCreated("password_resets"))
|
|
9
|
-
return;
|
|
10
|
-
let migrationContent = `import type { Database } from '@stacksjs/database'
|
|
11
|
-
`;
|
|
12
|
-
migrationContent += `import { sql } from '@stacksjs/database'
|
|
13
|
-
|
|
14
|
-
`;
|
|
15
|
-
migrationContent += `export async function up(db: Database<any>) {
|
|
16
|
-
`;
|
|
17
|
-
migrationContent += ` await db.schema
|
|
18
|
-
`;
|
|
19
|
-
migrationContent += ` .createTable('password_resets')
|
|
20
|
-
`;
|
|
21
|
-
migrationContent += ` .addColumn('email', 'varchar(255)', col => col.notNull())
|
|
22
|
-
`;
|
|
23
|
-
migrationContent += ` .addColumn('token', 'varchar(255)', col => col.notNull())
|
|
24
|
-
`;
|
|
25
|
-
migrationContent += ` .addColumn('created_at', 'timestamp', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
26
|
-
`;
|
|
27
|
-
migrationContent += ` .execute()
|
|
28
|
-
|
|
29
|
-
`;
|
|
30
|
-
migrationContent += ` await db.schema
|
|
31
|
-
`;
|
|
32
|
-
migrationContent += ` .createIndex('password_resets_email_index')
|
|
33
|
-
`;
|
|
34
|
-
migrationContent += ` .on('password_resets')
|
|
35
|
-
`;
|
|
36
|
-
migrationContent += ` .column('email')
|
|
37
|
-
`;
|
|
38
|
-
migrationContent += ` .execute()
|
|
39
|
-
|
|
40
|
-
`;
|
|
41
|
-
migrationContent += ` await db.schema
|
|
42
|
-
`;
|
|
43
|
-
migrationContent += ` .createIndex('password_resets_token_index')
|
|
44
|
-
`;
|
|
45
|
-
migrationContent += ` .on('password_resets')
|
|
46
|
-
`;
|
|
47
|
-
migrationContent += ` .column('token')
|
|
48
|
-
`;
|
|
49
|
-
migrationContent += ` .execute()
|
|
50
|
-
`;
|
|
51
|
-
migrationContent += `}
|
|
52
|
-
`;
|
|
53
|
-
const migrationFileName = `${new Date().getTime().toString()}-create-password-resets-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
|
|
54
|
-
await Bun.write(migrationFilePath, migrationContent);
|
|
55
|
-
log.success(`Created migration: ${italic(migrationFileName)}`);
|
|
56
|
-
}
|
|
57
|
-
export async function createPostgresPasswordResetsTable() {
|
|
58
|
-
if (await hasMigrationBeenCreated("password_resets"))
|
|
59
|
-
return;
|
|
60
|
-
let migrationContent = `import type { Database } from '@stacksjs/database'
|
|
61
|
-
`;
|
|
62
|
-
migrationContent += `import { sql } from '@stacksjs/database'
|
|
63
|
-
|
|
64
|
-
`;
|
|
65
|
-
migrationContent += `export async function up(db: Database<any>) {
|
|
66
|
-
`;
|
|
67
|
-
migrationContent += ` await db.schema
|
|
68
|
-
`;
|
|
69
|
-
migrationContent += ` .createTable('password_resets')
|
|
70
|
-
`;
|
|
71
|
-
migrationContent += ` .addColumn('email', 'varchar(255)', col => col.notNull())
|
|
72
|
-
`;
|
|
73
|
-
migrationContent += ` .addColumn('token', 'varchar(255)', col => col.notNull())
|
|
74
|
-
`;
|
|
75
|
-
migrationContent += ` .addColumn('created_at', 'timestamp', col => col.notNull().defaultTo(sql.raw('CURRENT_TIMESTAMP')))
|
|
76
|
-
`;
|
|
77
|
-
migrationContent += ` .execute()
|
|
78
|
-
|
|
79
|
-
`;
|
|
80
|
-
migrationContent += ` await db.schema
|
|
81
|
-
`;
|
|
82
|
-
migrationContent += ` .createIndex('password_resets_email_index')
|
|
83
|
-
`;
|
|
84
|
-
migrationContent += ` .on('password_resets')
|
|
85
|
-
`;
|
|
86
|
-
migrationContent += ` .column('email')
|
|
87
|
-
`;
|
|
88
|
-
migrationContent += ` .execute()
|
|
89
|
-
|
|
90
|
-
`;
|
|
91
|
-
migrationContent += ` await db.schema
|
|
92
|
-
`;
|
|
93
|
-
migrationContent += ` .createIndex('password_resets_token_index')
|
|
94
|
-
`;
|
|
95
|
-
migrationContent += ` .on('password_resets')
|
|
96
|
-
`;
|
|
97
|
-
migrationContent += ` .column('token')
|
|
98
|
-
`;
|
|
99
|
-
migrationContent += ` .execute()
|
|
100
|
-
`;
|
|
101
|
-
migrationContent += `}
|
|
102
|
-
`;
|
|
103
|
-
const migrationFileName = `${new Date().getTime().toString()}-create-password-resets-table.ts`, migrationFilePath = path.userMigrationsPath(migrationFileName);
|
|
104
|
-
await Bun.write(migrationFilePath, migrationContent);
|
|
105
|
-
log.success(`Created migration: ${italic(migrationFileName)}`);
|
|
106
|
-
}
|