@stacksjs/database 0.70.275 → 0.70.276
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 +6 -18
- package/dist/dialect.d.ts +8 -0
- package/dist/dialect.js +1 -1
- package/dist/notification-tables.js +2 -2
- package/dist/trait-tables.d.ts +2 -5
- package/dist/trait-tables.js +2 -2
- package/package.json +10 -10
package/dist/auth-tables.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import process from"node:process";import{randomBytes}from"node:crypto";import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";function getDbDriver(){return envVars.DB_CONNECTION||"sqlite"}export function usersEmailVerifiedAtSql(sql){return`ALTER TABLE users ADD COLUMN email_verified_at ${sql.nullableTimestamp}`}export function usersPasswordChangedAtSql(sql){return`ALTER TABLE users ADD COLUMN password_changed_at ${sql.nullableTimestamp}`}export function usersTwoFactorColumnsSql(sql){return["ALTER TABLE users ADD COLUMN two_factor_secret VARCHAR(255)",`ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN NOT NULL DEFAULT ${sql.boolFalse}`,"ALTER TABLE users ADD COLUMN two_factor_last_used_step BIGINT"]}export function usersStripeIdSql(){return"ALTER TABLE users ADD COLUMN stripe_id VARCHAR(255)"}export async function ensureUsersAuthColumns(sql,options={}){const alters=[usersEmailVerifiedAtSql(sql),usersPasswordChangedAtSql(sql),...usersTwoFactorColumnsSql(sql),usersStripeIdSql()];for(const alterSql of alters)try{await db.unsafe(alterSql).execute()}catch{if(options.verbose)log.debug(`[auth-tables] Skipped (already applied or users missing): ${alterSql}`)}try{await db.unsafe("CREATE UNIQUE INDEX IF NOT EXISTS idx_users_stripe_id ON users(stripe_id)").execute()}catch{if(options.verbose)log.debug("[auth-tables] Skipped users.stripe_id unique index (already applied or users missing)")}}export async function migrateAuthTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver),{isPostgres,boolTrue,now,pkColumn,nullableTimestamp,datetime}=sql;if(options.verbose)log.info(`Creating auth tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating oauth_clients table...");await db.unsafe(`
|
|
1
|
+
import process from"node:process";import{randomBytes}from"node:crypto";import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect}from"./dialect";function getDbDriver(){return envVars.DB_CONNECTION||"sqlite"}export function usersEmailVerifiedAtSql(sql){return`ALTER TABLE users ADD COLUMN email_verified_at ${sql.nullableTimestamp}`}export function usersPasswordChangedAtSql(sql){return`ALTER TABLE users ADD COLUMN password_changed_at ${sql.nullableTimestamp}`}export function usersTwoFactorColumnsSql(sql){return["ALTER TABLE users ADD COLUMN two_factor_secret VARCHAR(255)",`ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN NOT NULL DEFAULT ${sql.boolFalse}`,"ALTER TABLE users ADD COLUMN two_factor_last_used_step BIGINT"]}export function usersStripeIdSql(){return"ALTER TABLE users ADD COLUMN stripe_id VARCHAR(255)"}export async function ensureUsersAuthColumns(sql,options={}){const alters=[usersEmailVerifiedAtSql(sql),usersPasswordChangedAtSql(sql),...usersTwoFactorColumnsSql(sql),usersStripeIdSql()];for(const alterSql of alters)try{await db.unsafe(alterSql).execute()}catch{if(options.verbose)log.debug(`[auth-tables] Skipped (already applied or users missing): ${alterSql}`)}try{await db.unsafe(indexSqlForDialect("CREATE UNIQUE INDEX IF NOT EXISTS idx_users_stripe_id ON users(stripe_id)",getDbDriver())).execute()}catch{if(options.verbose)log.debug("[auth-tables] Skipped users.stripe_id unique index (already applied or users missing)")}}export async function migrateAuthTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver),{isPostgres,boolTrue,now,pkColumn,nullableTimestamp,datetime}=sql;if(options.verbose)log.info(`Creating auth tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating oauth_clients table...");await db.unsafe(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
3
3
|
${pkColumn},
|
|
4
4
|
name VARCHAR(255) NOT NULL,
|
|
@@ -40,9 +40,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
40
40
|
token VARCHAR(255) NOT NULL,
|
|
41
41
|
created_at ${datetime} DEFAULT CURRENT_TIMESTAMP
|
|
42
42
|
)
|
|
43
|
-
`).execute();try{await db.unsafe(`
|
|
44
|
-
CREATE INDEX IF NOT EXISTS idx_password_resets_email ON password_resets(email)
|
|
45
|
-
`).execute()}catch{}if(options.verbose)log.info("Creating passkeys table...");await db.unsafe(`
|
|
43
|
+
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE INDEX IF NOT EXISTS idx_password_resets_email ON password_resets(email)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating passkeys table...");await db.unsafe(`
|
|
46
44
|
CREATE TABLE IF NOT EXISTS passkeys (
|
|
47
45
|
id VARCHAR(255) PRIMARY KEY,
|
|
48
46
|
cred_public_key TEXT NOT NULL,
|
|
@@ -57,9 +55,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
57
55
|
created_at ${datetime} DEFAULT CURRENT_TIMESTAMP,
|
|
58
56
|
last_used_at ${nullableTimestamp}
|
|
59
57
|
)
|
|
60
|
-
`).execute();try{await db.unsafe(`
|
|
61
|
-
CREATE INDEX IF NOT EXISTS idx_passkeys_user_id ON passkeys(user_id)
|
|
62
|
-
`).execute()}catch{}if(options.verbose)log.info("Creating webauthn_challenges table...");await db.unsafe(`
|
|
58
|
+
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE INDEX IF NOT EXISTS idx_passkeys_user_id ON passkeys(user_id)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating webauthn_challenges table...");await db.unsafe(`
|
|
63
59
|
CREATE TABLE IF NOT EXISTS webauthn_challenges (
|
|
64
60
|
${pkColumn},
|
|
65
61
|
user_id INTEGER NOT NULL,
|
|
@@ -68,18 +64,14 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
68
64
|
expires_at ${datetime} NOT NULL,
|
|
69
65
|
created_at ${datetime} DEFAULT CURRENT_TIMESTAMP
|
|
70
66
|
)
|
|
71
|
-
`).execute();try{await db.unsafe(`
|
|
72
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_webauthn_challenges_user_purpose ON webauthn_challenges(user_id, purpose)
|
|
73
|
-
`).execute()}catch{}if(options.verbose)log.info("Creating two_factor_challenges table...");await db.unsafe(`
|
|
67
|
+
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE UNIQUE INDEX IF NOT EXISTS idx_webauthn_challenges_user_purpose ON webauthn_challenges(user_id, purpose)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating two_factor_challenges table...");await db.unsafe(`
|
|
74
68
|
CREATE TABLE IF NOT EXISTS two_factor_challenges (
|
|
75
69
|
id VARCHAR(255) PRIMARY KEY,
|
|
76
70
|
user_id INTEGER NOT NULL,
|
|
77
71
|
expires_at ${datetime} NOT NULL,
|
|
78
72
|
created_at ${datetime} DEFAULT CURRENT_TIMESTAMP
|
|
79
73
|
)
|
|
80
|
-
`).execute();try{await db.unsafe(`
|
|
81
|
-
CREATE INDEX IF NOT EXISTS idx_two_factor_challenges_user_id ON two_factor_challenges(user_id)
|
|
82
|
-
`).execute()}catch{}if(options.verbose)log.info("Creating two_factor_pending_secrets table...");await db.unsafe(`
|
|
74
|
+
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE INDEX IF NOT EXISTS idx_two_factor_challenges_user_id ON two_factor_challenges(user_id)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating two_factor_pending_secrets table...");await db.unsafe(`
|
|
83
75
|
CREATE TABLE IF NOT EXISTS two_factor_pending_secrets (
|
|
84
76
|
user_id INTEGER PRIMARY KEY,
|
|
85
77
|
secret VARCHAR(255) NOT NULL,
|
|
@@ -94,8 +86,4 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
94
86
|
`,["Personal Access Client",secret,"local","http://localhost",!0,!1,!1]).execute();else await db.unsafe(`
|
|
95
87
|
INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
|
|
96
88
|
VALUES (?, ?, ?, ?, ?, ?, ?, ${now})
|
|
97
|
-
`,["Personal Access Client",secret,"local","http://localhost",1,0,0]).execute();if(options.verbose)log.success("Personal access client created")}log.debug("Auth tables migrated successfully");return{success:!0}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);log.error("Failed to migrate auth tables:",errorMessage);return{success:!1,error:errorMessage}}}async function createTokenIndex(indexName,tableName,column){try{await db.unsafe(`
|
|
98
|
-
CREATE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${column}(255))
|
|
99
|
-
`).execute()}catch{try{await db.unsafe(`
|
|
100
|
-
CREATE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${column})
|
|
101
|
-
`).execute()}catch{}}}
|
|
89
|
+
`,["Personal Access Client",secret,"local","http://localhost",1,0,0]).execute();if(options.verbose)log.success("Personal access client created")}log.debug("Auth tables migrated successfully");return{success:!0}}catch(error){const errorMessage=error instanceof Error?error.message:String(error);log.error("Failed to migrate auth tables:",errorMessage);return{success:!1,error:errorMessage}}}async function createTokenIndex(indexName,tableName,column){const dbDriver=getDbDriver();try{await db.unsafe(indexSqlForDialect(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${column}(255))`,dbDriver)).execute()}catch{try{await db.unsafe(indexSqlForDialect(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${column})`,dbDriver)).execute()}catch{}}}
|
package/dist/dialect.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ export declare function isVitessSharded(explicit?: boolean | string): boolean;
|
|
|
16
16
|
export declare function dialectCapabilities(dialect: string, options?: DialectCapabilityOptions): DialectCapabilities;
|
|
17
17
|
/** Whether the framework has an explicit capability row for this dialect. */
|
|
18
18
|
export declare function isKnownDialect(dialect: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Render idempotent index DDL for the target dialect. MySQL-compatible
|
|
21
|
+
* servers reject `CREATE INDEX IF NOT EXISTS`; callers execute the bare form
|
|
22
|
+
* there and treat only the duplicate-index error as a successful replay.
|
|
23
|
+
*/
|
|
24
|
+
export declare function indexSqlForDialect(statement: string, dialect: string): string;
|
|
25
|
+
/** Whether an error is the expected result of replaying bare MySQL index DDL. */
|
|
26
|
+
export declare function isDuplicateIndexError(error: unknown): boolean;
|
|
19
27
|
/** Every dialect with a capability row, for CLI help and validation messages. */
|
|
20
28
|
export declare function knownDialects(): string[];
|
|
21
29
|
/**
|
package/dist/dialect.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import process from"node:process";const CAPABILITIES={sqlite:{dialect:"sqlite",wire:"sqlite",queryBuilderDialect:"sqlite",identifierQuote:'"',supportsForeignKeys:!0,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!0,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!0},mysql:{dialect:"mysql",wire:"mysql",queryBuilderDialect:"mysql",defaultPort:3306,identifierQuote:"`",supportsForeignKeys:!0,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!0,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!1},singlestore:{dialect:"singlestore",wire:"mysql",queryBuilderDialect:"singlestore",defaultPort:3306,identifierQuote:"`",supportsForeignKeys:!1,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!1,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!1},vitess:{dialect:"vitess",wire:"mysql",queryBuilderDialect:"vitess",defaultPort:15306,identifierQuote:"`",supportsForeignKeys:!1,supportsAutoIncrement:!1,supportsAtomicMultiTableTransactions:!1,requiresOnlineDdl:!0,supportsCreateIndexIfNotExists:!1},postgres:{dialect:"postgres",wire:"postgres",queryBuilderDialect:"postgres",defaultPort:5432,identifierQuote:'"',supportsForeignKeys:!0,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!0,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!0}};export function isVitessSharded(explicit){if(typeof explicit==="boolean")return explicit;const raw=(typeof explicit==="string"?explicit:process.env.DB_VITESS_SHARDED)?.trim().toLowerCase();if(raw===void 0||raw==="")return!0;return!["0","false","no","off"].includes(raw)}export function dialectCapabilities(dialect,options={}){const caps=CAPABILITIES[dialect]??CAPABILITIES.sqlite;if(dialect!=="vitess"||isVitessSharded(options.vitessSharded))return caps;return{...CAPABILITIES.mysql,dialect:"vitess",queryBuilderDialect:"vitess",defaultPort:15306}}export function isKnownDialect(dialect){return dialect in CAPABILITIES}export function knownDialects(){return Object.keys(CAPABILITIES)}export function isMysqlWire(dialect){return dialectCapabilities(dialect).wire==="mysql"}export function isPostgresWire(dialect){return dialectCapabilities(dialect).wire==="postgres"}export function toQueryBuilderDialect(dialect){return dialectCapabilities(dialect).queryBuilderDialect}
|
|
1
|
+
import process from"node:process";const CAPABILITIES={sqlite:{dialect:"sqlite",wire:"sqlite",queryBuilderDialect:"sqlite",identifierQuote:'"',supportsForeignKeys:!0,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!0,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!0},mysql:{dialect:"mysql",wire:"mysql",queryBuilderDialect:"mysql",defaultPort:3306,identifierQuote:"`",supportsForeignKeys:!0,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!0,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!1},singlestore:{dialect:"singlestore",wire:"mysql",queryBuilderDialect:"singlestore",defaultPort:3306,identifierQuote:"`",supportsForeignKeys:!1,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!1,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!1},vitess:{dialect:"vitess",wire:"mysql",queryBuilderDialect:"vitess",defaultPort:15306,identifierQuote:"`",supportsForeignKeys:!1,supportsAutoIncrement:!1,supportsAtomicMultiTableTransactions:!1,requiresOnlineDdl:!0,supportsCreateIndexIfNotExists:!1},postgres:{dialect:"postgres",wire:"postgres",queryBuilderDialect:"postgres",defaultPort:5432,identifierQuote:'"',supportsForeignKeys:!0,supportsAutoIncrement:!0,supportsAtomicMultiTableTransactions:!0,requiresOnlineDdl:!1,supportsCreateIndexIfNotExists:!0}};export function isVitessSharded(explicit){if(typeof explicit==="boolean")return explicit;const raw=(typeof explicit==="string"?explicit:process.env.DB_VITESS_SHARDED)?.trim().toLowerCase();if(raw===void 0||raw==="")return!0;return!["0","false","no","off"].includes(raw)}export function dialectCapabilities(dialect,options={}){const caps=CAPABILITIES[dialect]??CAPABILITIES.sqlite;if(dialect!=="vitess"||isVitessSharded(options.vitessSharded))return caps;return{...CAPABILITIES.mysql,dialect:"vitess",queryBuilderDialect:"vitess",defaultPort:15306}}export function isKnownDialect(dialect){return dialect in CAPABILITIES}export function indexSqlForDialect(statement,dialect){if(dialectCapabilities(dialect).supportsCreateIndexIfNotExists)return statement;return statement.replace(/^(\s*CREATE\s+(?:UNIQUE\s+)?INDEX)\s+IF\s+NOT\s+EXISTS\s+/i,"$1 ")}export function isDuplicateIndexError(error){const message=error instanceof Error?error.message:String(error);return/duplicate key name|already exists/i.test(message)}export function knownDialects(){return Object.keys(CAPABILITIES)}export function isMysqlWire(dialect){return dialectCapabilities(dialect).wire==="mysql"}export function isPostgresWire(dialect){return dialectCapabilities(dialect).wire==="postgres"}export function toQueryBuilderDialect(dialect){return dialectCapabilities(dialect).queryBuilderDialect}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";function getDbDriver(){return process.env.DB_CONNECTION||envVars.DB_CONNECTION||"sqlite"}export function notificationsTableSql(sql){const{pkColumn,nullableTimestamp,datetime}=sql;return`CREATE TABLE IF NOT EXISTS notifications (
|
|
1
|
+
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect,isDuplicateIndexError}from"./dialect";function getDbDriver(){return process.env.DB_CONNECTION||envVars.DB_CONNECTION||"sqlite"}export function notificationsTableSql(sql){const{pkColumn,nullableTimestamp,datetime}=sql;return`CREATE TABLE IF NOT EXISTS notifications (
|
|
2
2
|
${pkColumn},
|
|
3
3
|
user_id INTEGER NOT NULL,
|
|
4
4
|
type VARCHAR(255) NOT NULL,
|
|
@@ -29,4 +29,4 @@ import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";imp
|
|
|
29
29
|
sent_at ${nullableTimestamp},
|
|
30
30
|
created_at ${datetime} DEFAULT CURRENT_TIMESTAMP,
|
|
31
31
|
updated_at ${nullableTimestamp}
|
|
32
|
-
)`}export async function migrateNotificationTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver);if(options.verbose)log.info(`Creating notification tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating notifications table...");await db.unsafe(notificationsTableSql(sql)).execute();await
|
|
32
|
+
)`}async function createIndex(statement,dialect){try{await db.unsafe(indexSqlForDialect(statement,dialect)).execute()}catch(error){if(!isDuplicateIndexError(error))throw error}}export async function migrateNotificationTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver);if(options.verbose)log.info(`Creating notification tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating notifications table...");await db.unsafe(notificationsTableSql(sql)).execute();await createIndex("CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications (user_id)",dbDriver);if(options.verbose)log.info("Creating notification_preferences table...");await db.unsafe(notificationPreferencesTableSql(sql)).execute();await createIndex("CREATE INDEX IF NOT EXISTS idx_notification_preferences_user ON notification_preferences (user_id)",dbDriver);if(options.verbose)log.info("Creating notification deliveries table...");await db.unsafe(notificationDeliveriesTableSql(sql)).execute();await createIndex("CREATE INDEX IF NOT EXISTS idx_notification_deliveries_channel ON notification_deliveries (channel)",dbDriver);await createIndex("CREATE INDEX IF NOT EXISTS idx_notification_deliveries_status ON notification_deliveries (status)",dbDriver);if(options.verbose)log.success("Notification tables created");return{success:!0}}catch(error){const message=error instanceof Error?error.message:String(error);log.error(`Failed to create notification tables: ${message}`);return{success:!1,error:message}}}
|
package/dist/trait-tables.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { indexSqlForDialect } from './dialect';
|
|
1
2
|
import { sqlHelpers } from './sql-helpers';
|
|
2
3
|
/**
|
|
3
4
|
* The tables {@link migrateTraitTables} owns.
|
|
@@ -96,11 +97,6 @@ export declare function traitTableIndexSql(): string[];
|
|
|
96
97
|
* deadlock bun's module loader (see `drivers/helpers.ts`).
|
|
97
98
|
*/
|
|
98
99
|
export declare function likeableTargets(): Promise<Array<{ table: string, foreignKey: string }>>;
|
|
99
|
-
/**
|
|
100
|
-
* Strip `IF NOT EXISTS` for dialects that reject it on `CREATE INDEX`, and let
|
|
101
|
-
* {@link isDuplicateIndexError} absorb the replay there instead.
|
|
102
|
-
*/
|
|
103
|
-
export declare function indexSqlForDialect(statement: string, dialect: string): string;
|
|
104
100
|
/**
|
|
105
101
|
* Create the polymorphic trait tables. Idempotent (`IF NOT EXISTS`), so it's
|
|
106
102
|
* safe to run on every `buddy migrate`.
|
|
@@ -124,3 +120,4 @@ export declare function migrateTraitTables(options?: { verbose?: boolean }): Pro
|
|
|
124
120
|
*/
|
|
125
121
|
export declare const UNSCOPED_OWNER_ID: 0;
|
|
126
122
|
declare type SqlHelpers = ReturnType<typeof sqlHelpers>;
|
|
123
|
+
export { indexSqlForDialect } from './dialect';
|
package/dist/trait-tables.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{
|
|
1
|
+
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect,isDuplicateIndexError}from"./dialect";export{indexSqlForDialect}from"./dialect";function getDbDriver(){return process.env.DB_CONNECTION||envVars.DB_CONNECTION||"sqlite"}function createdAt(sql){return`created_at ${sql.datetime}`}function updatedAt(sql){return`updated_at ${sql.nullableTimestamp}`}export function traitTableNames(){return["commentables","taggables","categorizables","commentable_upvotes","taggable_models","categorizable_models"]}export const UNSCOPED_OWNER_ID=0;export function commentablesTableSql(sql){const{pkColumn,boolTrue}=sql;return`CREATE TABLE IF NOT EXISTS commentables (
|
|
2
2
|
${pkColumn},
|
|
3
3
|
title VARCHAR(255) NOT NULL,
|
|
4
4
|
body TEXT NOT NULL,
|
|
@@ -58,4 +58,4 @@ import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";imp
|
|
|
58
58
|
upvoteable_id INTEGER NOT NULL,
|
|
59
59
|
upvoteable_type VARCHAR(255) NOT NULL,
|
|
60
60
|
${createdAt(sql)}
|
|
61
|
-
)`}export function traitTableIndexSql(){return["CREATE INDEX IF NOT EXISTS commentables_owner_index ON commentables (commentables_type, commentables_id)","CREATE INDEX IF NOT EXISTS commentables_status_index ON commentables (status)","CREATE UNIQUE INDEX IF NOT EXISTS taggables_owner_slug_unique ON taggables (taggable_type, taggable_id, slug)","CREATE UNIQUE INDEX IF NOT EXISTS categorizables_owner_slug_unique ON categorizables (categorizable_type, categorizable_id, slug)","CREATE INDEX IF NOT EXISTS commentable_upvotes_target_index ON commentable_upvotes (upvoteable_type, upvoteable_id)","CREATE UNIQUE INDEX IF NOT EXISTS commentable_upvotes_user_unique ON commentable_upvotes (upvoteable_type, upvoteable_id, user_id)","CREATE UNIQUE INDEX IF NOT EXISTS taggable_models_unique ON taggable_models (tag_id, taggable_id, taggable_type)","CREATE UNIQUE INDEX IF NOT EXISTS categorizable_models_unique ON categorizable_models (category_id, categorizable_id, categorizable_type)"]}export async function likeableTargets(){const{path}=await import("@stacksjs/path"),{globSync}=await import("@stacksjs/storage"),{getTableName}=await import("@stacksjs/orm"),{getLikeableForeignKey,getUpvoteTableName}=await import("./drivers/helpers"),modelFiles=globSync([path.userModelsPath("*.ts"),path.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0}),targets=new Map;for(const modelFile of modelFiles){let model;try{model=(await import(modelFile)).default}catch{continue}if(!model?.traits?.likeable)continue;const tableName=await getTableName(model,modelFile);if(!tableName)continue;const table=getUpvoteTableName(model,tableName);if(!table||!/^[a-z_]\w*$/i.test(table))continue;const foreignKey=getLikeableForeignKey(model,tableName);if(!/^[a-z_]\w*$/i.test(foreignKey))continue;targets.set(table,{table,foreignKey})}return[...targets.values()]}export
|
|
61
|
+
)`}export function traitTableIndexSql(){return["CREATE INDEX IF NOT EXISTS commentables_owner_index ON commentables (commentables_type, commentables_id)","CREATE INDEX IF NOT EXISTS commentables_status_index ON commentables (status)","CREATE UNIQUE INDEX IF NOT EXISTS taggables_owner_slug_unique ON taggables (taggable_type, taggable_id, slug)","CREATE UNIQUE INDEX IF NOT EXISTS categorizables_owner_slug_unique ON categorizables (categorizable_type, categorizable_id, slug)","CREATE INDEX IF NOT EXISTS commentable_upvotes_target_index ON commentable_upvotes (upvoteable_type, upvoteable_id)","CREATE UNIQUE INDEX IF NOT EXISTS commentable_upvotes_user_unique ON commentable_upvotes (upvoteable_type, upvoteable_id, user_id)","CREATE UNIQUE INDEX IF NOT EXISTS taggable_models_unique ON taggable_models (tag_id, taggable_id, taggable_type)","CREATE UNIQUE INDEX IF NOT EXISTS categorizable_models_unique ON categorizable_models (category_id, categorizable_id, categorizable_type)"]}export async function likeableTargets(){const{path}=await import("@stacksjs/path"),{globSync}=await import("@stacksjs/storage"),{getTableName}=await import("@stacksjs/orm"),{getLikeableForeignKey,getUpvoteTableName}=await import("./drivers/helpers"),modelFiles=globSync([path.userModelsPath("*.ts"),path.storagePath("framework/defaults/app/Models/**/*.ts")],{absolute:!0}),targets=new Map;for(const modelFile of modelFiles){let model;try{model=(await import(modelFile)).default}catch{continue}if(!model?.traits?.likeable)continue;const tableName=await getTableName(model,modelFile);if(!tableName)continue;const table=getUpvoteTableName(model,tableName);if(!table||!/^[a-z_]\w*$/i.test(table))continue;const foreignKey=getLikeableForeignKey(model,tableName);if(!/^[a-z_]\w*$/i.test(foreignKey))continue;targets.set(table,{table,foreignKey})}return[...targets.values()]}export async function migrateTraitTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver);if(options.verbose)log.info(`Creating polymorphic trait tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating commentables table...");await db.unsafe(commentablesTableSql(sql)).execute();if(options.verbose)log.info("Creating taggables table...");await db.unsafe(taggablesTableSql(sql)).execute();if(options.verbose)log.info("Creating categorizables table...");await db.unsafe(categorizablesTableSql(sql)).execute();if(options.verbose)log.info("Creating commentable_upvotes table...");await db.unsafe(commentableUpvotesTableSql(sql)).execute();if(options.verbose)log.info("Creating taggable_models pivot...");await db.unsafe(taggableModelsTableSql(sql)).execute();if(options.verbose)log.info("Creating categorizable_models pivot...");await db.unsafe(categorizableModelsTableSql(sql)).execute();try{for(const{table,foreignKey}of await likeableTargets()){if(options.verbose)log.info(`Creating ${table} table...`);await db.unsafe(likesTableSql(sql,table,foreignKey)).execute()}}catch(error){log.debug(`[trait-tables] Skipped likeable tables: ${error instanceof Error?error.message:String(error)}`)}for(const statement of traitTableIndexSql())try{await db.unsafe(indexSqlForDialect(statement,dbDriver)).execute()}catch(error){if(!isDuplicateIndexError(error))throw error}if(options.verbose)log.success("Polymorphic trait tables created");return{success:!0}}catch(error){const message=error instanceof Error?error.message:String(error);log.error(`Failed to create polymorphic trait tables: ${message}`);return{success:!1,error:message}}}
|
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.276",
|
|
6
6
|
"description": "The Stacks database integration.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -65,15 +65,15 @@
|
|
|
65
65
|
"dynamodb-tooling": "^0.3.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@stacksjs/cli": "0.70.
|
|
69
|
-
"@stacksjs/config": "0.70.
|
|
70
|
-
"@stacksjs/logging": "0.70.
|
|
71
|
-
"@stacksjs/router": "0.70.
|
|
68
|
+
"@stacksjs/cli": "0.70.276",
|
|
69
|
+
"@stacksjs/config": "0.70.276",
|
|
70
|
+
"@stacksjs/logging": "0.70.276",
|
|
71
|
+
"@stacksjs/router": "0.70.276",
|
|
72
72
|
"better-dx": "^0.2.17",
|
|
73
|
-
"@stacksjs/path": "0.70.
|
|
74
|
-
"@stacksjs/query-builder": "0.70.
|
|
75
|
-
"@stacksjs/storage": "0.70.
|
|
76
|
-
"@stacksjs/strings": "0.70.
|
|
77
|
-
"@stacksjs/utils": "0.70.
|
|
73
|
+
"@stacksjs/path": "0.70.276",
|
|
74
|
+
"@stacksjs/query-builder": "0.70.276",
|
|
75
|
+
"@stacksjs/storage": "0.70.276",
|
|
76
|
+
"@stacksjs/strings": "0.70.276",
|
|
77
|
+
"@stacksjs/utils": "0.70.276"
|
|
78
78
|
}
|
|
79
79
|
}
|