@stacksjs/database 0.70.258 → 0.70.260

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/auth-tables.js +18 -137
  2. package/dist/column.js +1 -26
  3. package/dist/custom/audits.js +20 -54
  4. package/dist/custom/errors.js +16 -46
  5. package/dist/custom/index.js +1 -3
  6. package/dist/custom/jobs.js +13 -137
  7. package/dist/database.js +1 -181
  8. package/dist/datetime-columns.js +2 -79
  9. package/dist/ddl-constraints.js +7 -111
  10. package/dist/defaults.js +1 -48
  11. package/dist/dialect.js +1 -79
  12. package/dist/driver-config.js +1 -172
  13. package/dist/drivers/defaults/index.js +1 -1
  14. package/dist/drivers/defaults/traits.js +1 -29
  15. package/dist/drivers/dynamodb.js +1 -607
  16. package/dist/drivers/helpers.js +1 -206
  17. package/dist/drivers/index.js +1 -9
  18. package/dist/drivers/mysql.js +58 -299
  19. package/dist/drivers/postgres.js +78 -368
  20. package/dist/drivers/sqlite.js +61 -379
  21. package/dist/ensure-database.js +1 -145
  22. package/dist/fk-audit.js +3 -187
  23. package/dist/index.js +1 -64
  24. package/dist/managed-columns.js +1 -59
  25. package/dist/migration-dialect.js +4 -107
  26. package/dist/migration-ledger.js +1 -382
  27. package/dist/migration-lock.js +1 -143
  28. package/dist/migrations.js +15 -1118
  29. package/dist/model-sources.js +1 -76
  30. package/dist/notification-tables.js +4 -49
  31. package/dist/query-logger.js +2 -241
  32. package/dist/query-parser.js +1 -93
  33. package/dist/rbac-tables.js +6 -61
  34. package/dist/relation-columns.js +1 -66
  35. package/dist/replicas.js +1 -74
  36. package/dist/safe-migrations.js +2 -52
  37. package/dist/schema.js +1 -10
  38. package/dist/seeder.js +1 -457
  39. package/dist/sql-helpers.js +1 -50
  40. package/dist/table.js +1 -26
  41. package/dist/tools/setup.js +1 -6
  42. package/dist/trait-tables.js +8 -153
  43. package/dist/transaction-context.js +1 -62
  44. package/dist/types.js +1 -98
  45. package/dist/unique-audit.js +3 -155
  46. package/dist/utils.js +1 -285
  47. package/dist/uuid-columns.js +1 -68
  48. package/dist/validators.js +1 -122
  49. package/dist/vschema.js +2 -121
  50. package/package.json +20 -13
@@ -1,66 +1 @@
1
- import { getTableName } from "@stacksjs/orm";
2
- import { path } from "@stacksjs/path";
3
- import { fs } from "@stacksjs/storage";
4
- function snakeCase(str) {
5
- return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/(\d)([A-Za-z])/g, "$1_$2").toLowerCase();
6
- }
7
- export function belongsToColumn(entry) {
8
- if (typeof entry === "string")
9
- return entry.length > 0 ? `${snakeCase(entry)}_id` : null;
10
- if (entry && typeof entry === "object") {
11
- const relation = entry;
12
- if (typeof relation.foreignKey === "string" && relation.foreignKey.length > 0)
13
- return relation.foreignKey;
14
- if (typeof relation.model === "string" && relation.model.length > 0)
15
- return `${snakeCase(relation.model)}_id`;
16
- }
17
- return null;
18
- }
19
- export function belongsToColumnsOf(model) {
20
- const declared = model.belongsTo;
21
- if (!declared)
22
- return [];
23
- const entries = Array.isArray(declared) ? declared : Object.entries(declared).map(([model, value]) => value && typeof value === "object" ? { model, ...value } : model), columns = [];
24
- for (const entry of entries) {
25
- const column = belongsToColumn(entry);
26
- if (column)
27
- columns.push(column);
28
- }
29
- return columns;
30
- }
31
- async function loadModelsFrom(dir) {
32
- const out = [];
33
- if (!fs.existsSync(dir))
34
- return out;
35
- for (const entry of fs.readdirSync(dir, { withFileTypes: !0 })) {
36
- const fullPath = path.join(dir, entry.name);
37
- if (entry.isDirectory()) {
38
- out.push(...await loadModelsFrom(fullPath));
39
- continue;
40
- }
41
- if (!entry.name.endsWith(".ts"))
42
- continue;
43
- if (entry.name.startsWith("_") || entry.name.startsWith("index"))
44
- continue;
45
- try {
46
- const imported = (await import(fullPath)).default;
47
- if (imported?.name || imported?.table)
48
- out.push({ filePath: fullPath, model: imported });
49
- } catch {}
50
- }
51
- return out;
52
- }
53
- export async function findRelationForeignKeys() {
54
- const dirs = [path.userModelsPath(), path.frameworkPath("defaults/app/Models")], byTable = new Map;
55
- for (const dir of dirs)
56
- for (const { filePath, model } of await loadModelsFrom(dir)) {
57
- const columns = belongsToColumnsOf(model);
58
- if (columns.length === 0)
59
- continue;
60
- const table = getTableName(model, filePath), existing = byTable.get(table) ?? new Set;
61
- for (const column of columns)
62
- existing.add(column);
63
- byTable.set(table, existing);
64
- }
65
- return byTable;
66
- }
1
+ import{getTableName}from"@stacksjs/orm";import{path}from"@stacksjs/path";import{fs}from"@stacksjs/storage";function snakeCase(str){return str.replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/(\d)([A-Za-z])/g,"$1_$2").toLowerCase()}export function belongsToColumn(entry){if(typeof entry==="string")return entry.length>0?`${snakeCase(entry)}_id`:null;if(entry&&typeof entry==="object"){const relation=entry;if(typeof relation.foreignKey==="string"&&relation.foreignKey.length>0)return relation.foreignKey;if(typeof relation.model==="string"&&relation.model.length>0)return`${snakeCase(relation.model)}_id`}return null}export function belongsToColumnsOf(model){const declared=model.belongsTo;if(!declared)return[];const entries=Array.isArray(declared)?declared:Object.entries(declared).map(([model,value])=>value&&typeof value==="object"?{model,...value}:model),columns=[];for(const entry of entries){const column=belongsToColumn(entry);if(column)columns.push(column)}return columns}async function loadModelsFrom(dir){const out=[];if(!fs.existsSync(dir))return out;for(const entry of fs.readdirSync(dir,{withFileTypes:!0})){const fullPath=path.join(dir,entry.name);if(entry.isDirectory()){out.push(...await loadModelsFrom(fullPath));continue}if(!entry.name.endsWith(".ts"))continue;if(entry.name.startsWith("_")||entry.name.startsWith("index"))continue;try{const imported=(await import(fullPath)).default;if(imported?.name||imported?.table)out.push({filePath:fullPath,model:imported})}catch{}}return out}export async function findRelationForeignKeys(){const dirs=[path.userModelsPath(),path.frameworkPath("defaults/app/Models")],byTable=new Map;for(const dir of dirs)for(const{filePath,model}of await loadModelsFrom(dir)){const columns=belongsToColumnsOf(model);if(columns.length===0)continue;const table=getTableName(model,filePath),existing=byTable.get(table)??new Set;for(const column of columns)existing.add(column);byTable.set(table,existing)}return byTable}
package/dist/replicas.js CHANGED
@@ -1,74 +1 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- const routingContext = new AsyncLocalStorage;
3
- export function withRoutingContext(fn) {
4
- return routingContext.run({ wroteInContext: !1, inTransaction: !1 }, fn);
5
- }
6
- export function markContextWrote() {
7
- const store = routingContext.getStore();
8
- if (store)
9
- store.wroteInContext = !0;
10
- }
11
- export async function withTransactionContext(fn) {
12
- const store = routingContext.getStore();
13
- if (!store)
14
- return fn();
15
- const previous = store.inTransaction;
16
- store.inTransaction = !0;
17
- try {
18
- return await fn();
19
- } finally {
20
- store.inTransaction = previous;
21
- }
22
- }
23
- export function contextHasWritten() {
24
- return routingContext.getStore()?.wroteInContext ?? !1;
25
- }
26
- export function contextInTransaction() {
27
- return routingContext.getStore()?.inTransaction ?? !1;
28
- }
29
- export function shouldRouteToReplica(options) {
30
- const { policy, replicas } = options;
31
- if (!replicas?.length)
32
- return !1;
33
- if (!policy?.autoRoute)
34
- return !1;
35
- if (contextInTransaction())
36
- return !1;
37
- if (contextHasWritten())
38
- return !1;
39
- return !0;
40
- }
41
- let roundRobinCursor = 0;
42
- export function resetReplicaCursor() {
43
- roundRobinCursor = 0;
44
- }
45
- export function selectReplica(replicas, strategy = "round-robin", random = Math.random) {
46
- if (!replicas.length)
47
- return;
48
- if (replicas.length === 1)
49
- return replicas[0];
50
- if (strategy === "random")
51
- return replicas[Math.floor(random() * replicas.length)];
52
- if (strategy === "weighted") {
53
- const weights = replicas.map((r) => Math.max(0, r.weight ?? 1)), total = weights.reduce((sum, w) => sum + w, 0);
54
- if (total <= 0)
55
- return replicas[roundRobinCursor++ % replicas.length];
56
- let ticket = random() * total;
57
- for (let i = 0;i < replicas.length; i++) {
58
- ticket -= weights[i];
59
- if (ticket < 0)
60
- return replicas[i];
61
- }
62
- return replicas[replicas.length - 1];
63
- }
64
- return replicas[roundRobinCursor++ % replicas.length];
65
- }
66
- export function resolveReplicaConnection(replica, primary) {
67
- return {
68
- database: primary.name ?? primary.database ?? "",
69
- host: replica.host,
70
- port: replica.port ?? primary.port,
71
- username: replica.username ?? primary.username,
72
- password: replica.password ?? primary.password
73
- };
74
- }
1
+ import{AsyncLocalStorage}from"node:async_hooks";const routingContext=new AsyncLocalStorage;export function withRoutingContext(fn){return routingContext.run({wroteInContext:!1,inTransaction:!1},fn)}export function markContextWrote(){const store=routingContext.getStore();if(store)store.wroteInContext=!0}export async function withTransactionContext(fn){const store=routingContext.getStore();if(!store)return fn();const previous=store.inTransaction;store.inTransaction=!0;try{return await fn()}finally{store.inTransaction=previous}}export function contextHasWritten(){return routingContext.getStore()?.wroteInContext??!1}export function contextInTransaction(){return routingContext.getStore()?.inTransaction??!1}export function shouldRouteToReplica(options){const{policy,replicas}=options;if(!replicas?.length)return!1;if(!policy?.autoRoute)return!1;if(contextInTransaction())return!1;if(contextHasWritten())return!1;return!0}let roundRobinCursor=0;export function resetReplicaCursor(){roundRobinCursor=0}export function selectReplica(replicas,strategy="round-robin",random=Math.random){if(!replicas.length)return;if(replicas.length===1)return replicas[0];if(strategy==="random")return replicas[Math.floor(random()*replicas.length)];if(strategy==="weighted"){const weights=replicas.map((r)=>Math.max(0,r.weight??1)),total=weights.reduce((sum,w)=>sum+w,0);if(total<=0)return replicas[roundRobinCursor++%replicas.length];let ticket=random()*total;for(let i=0;i<replicas.length;i++){ticket-=weights[i];if(ticket<0)return replicas[i]}return replicas[replicas.length-1]}return replicas[roundRobinCursor++%replicas.length]}export function resolveReplicaConnection(replica,primary){return{database:primary.name??primary.database??"",host:replica.host,port:replica.port??primary.port,username:replica.username??primary.username,password:replica.password??primary.password}}
@@ -1,24 +1,4 @@
1
- import { db } from "./utils";
2
- import { sql } from "./types";
3
- export async function addColumnSafely(db, tableName, columnName, options) {
4
- const { type, defaultValue, notNull = !1, batchSize = 1000 } = options, dbAny = db, defaultSql = defaultValue === void 0 ? "" : ` DEFAULT ${formatDefault(defaultValue)}`;
5
- await execRaw(dbAny, `ALTER TABLE ${quote(tableName)} ADD COLUMN ${quote(columnName)} ${type}${defaultSql}`);
6
- if (defaultValue !== void 0)
7
- await backfillInBatches(db, tableName, columnName, defaultValue, batchSize);
8
- if (notNull)
9
- await execRaw(dbAny, `ALTER TABLE ${quote(tableName)} ALTER COLUMN ${quote(columnName)} SET NOT NULL`);
10
- }
11
- async function execRaw(dbAny, statement) {
12
- if (typeof dbAny.unsafe === "function")
13
- return await dbAny.unsafe(statement) ?? {};
14
- await sql`${sql.raw(statement)}`.execute(dbAny);
15
- return {};
16
- }
17
- export async function backfillInBatches(db, tableName, columnName, value, batchSize = 1000) {
18
- const dbAny = db;
19
- let updated = 0, total = 0;
20
- do {
21
- const batchSql = `
1
+ import{db}from"./utils";import{sql}from"./types";export async function addColumnSafely(db,tableName,columnName,options){const{type,defaultValue,notNull=!1,batchSize=1000}=options,dbAny=db,defaultSql=defaultValue===void 0?"":` DEFAULT ${formatDefault(defaultValue)}`;await execRaw(dbAny,`ALTER TABLE ${quote(tableName)} ADD COLUMN ${quote(columnName)} ${type}${defaultSql}`);if(defaultValue!==void 0)await backfillInBatches(db,tableName,columnName,defaultValue,batchSize);if(notNull)await execRaw(dbAny,`ALTER TABLE ${quote(tableName)} ALTER COLUMN ${quote(columnName)} SET NOT NULL`)}async function execRaw(dbAny,statement){if(typeof dbAny.unsafe==="function")return await dbAny.unsafe(statement)??{};await sql`${sql.raw(statement)}`.execute(dbAny);return{}}export async function backfillInBatches(db,tableName,columnName,value,batchSize=1000){const dbAny=db;let updated=0,total=0;do{const batchSql=`
22
2
  UPDATE ${quote(tableName)} SET ${quote(columnName)} = ${formatDefault(value)}
23
3
  WHERE ${quote(columnName)} IS NULL
24
4
  AND ${rowIdColumnFor(dbAny)} IN (
@@ -26,34 +6,4 @@ export async function backfillInBatches(db, tableName, columnName, value, batchS
26
6
  WHERE ${quote(columnName)} IS NULL
27
7
  LIMIT ${batchSize}
28
8
  )
29
- `, result = await execRaw(dbAny, batchSql);
30
- updated = result.numAffectedRows != null ? Number(result.numAffectedRows) : 0;
31
- total += updated;
32
- } while (updated > 0);
33
- }
34
- export async function renameColumnSafely(db, tableName, oldName, newName, options) {
35
- const dbAny = db;
36
- if (options.atomic) {
37
- await execRaw(dbAny, `ALTER TABLE ${quote(tableName)} RENAME COLUMN ${quote(oldName)} TO ${quote(newName)}`);
38
- return;
39
- }
40
- await execRaw(dbAny, `ALTER TABLE ${quote(tableName)} ADD COLUMN ${quote(newName)} ${options.type}`);
41
- await execRaw(dbAny, `UPDATE ${quote(tableName)} SET ${quote(newName)} = ${quote(oldName)}`);
42
- }
43
- function quote(name) {
44
- if (!/^[a-z_][a-z0-9_]*$/i.test(name))
45
- throw Error(`Refusing to quote unsafe identifier: ${JSON.stringify(name)}`);
46
- return `"${name}"`;
47
- }
48
- function formatDefault(value) {
49
- if (value === null)
50
- return "NULL";
51
- if (typeof value === "number")
52
- return String(value);
53
- if (typeof value === "boolean")
54
- return value ? "TRUE" : "FALSE";
55
- return `'${String(value).replace(/'/g, "''")}'`;
56
- }
57
- function rowIdColumnFor(_db) {
58
- return "id";
59
- }
9
+ `,result=await execRaw(dbAny,batchSql);updated=result.numAffectedRows!=null?Number(result.numAffectedRows):0;total+=updated}while(updated>0)}export async function renameColumnSafely(db,tableName,oldName,newName,options){const dbAny=db;if(options.atomic){await execRaw(dbAny,`ALTER TABLE ${quote(tableName)} RENAME COLUMN ${quote(oldName)} TO ${quote(newName)}`);return}await execRaw(dbAny,`ALTER TABLE ${quote(tableName)} ADD COLUMN ${quote(newName)} ${options.type}`);await execRaw(dbAny,`UPDATE ${quote(tableName)} SET ${quote(newName)} = ${quote(oldName)}`)}function quote(name){if(!/^[a-z_][a-z0-9_]*$/i.test(name))throw Error(`Refusing to quote unsafe identifier: ${JSON.stringify(name)}`);return`"${name}"`}function formatDefault(value){if(value===null)return"NULL";if(typeof value==="number")return String(value);if(typeof value==="boolean")return value?"TRUE":"FALSE";return`'${String(value).replace(/'/g,"''")}'`}function rowIdColumnFor(_db){return"id"}
package/dist/schema.js CHANGED
@@ -1,10 +1 @@
1
- import { log } from "@stacksjs/logging";
2
- import { Table } from "./table";
3
- export const Schema = {
4
- async createTable(tableName, callback) {
5
- const table = new Table;
6
- callback(table);
7
- table.execute();
8
- log.success(`Table "${tableName}" created.`);
9
- }
10
- };
1
+ import{log}from"@stacksjs/logging";import{Table}from"./table";export const Schema={async createTable(tableName,callback){const table=new Table;callback(table);table.execute();log.success(`Table "${tableName}" created.`)}};