@stacksjs/database 0.75.299 → 0.75.301
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 +18 -0
- package/dist/migrations.js +1 -1
- package/dist/schema-align.d.ts +4 -1
- package/dist/schema-align.js +2 -2
- package/dist/trait-tables.js +1 -1
- package/dist/utils.d.ts +7 -0
- package/dist/utils.js +1 -1
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -28,6 +28,24 @@ import * as database from '@stacksjs/database'
|
|
|
28
28
|
|
|
29
29
|
Learn more in the docs.
|
|
30
30
|
|
|
31
|
+
## Legacy SQLite trait pivots
|
|
32
|
+
|
|
33
|
+
`buddy migrate` and `buddy migrate:fresh` reconcile missing `taggable_id` or
|
|
34
|
+
`categorizable_id` columns in empty legacy SQLite pivot tables before creating
|
|
35
|
+
trait indexes. Added owner columns remain required and have no default value.
|
|
36
|
+
Existing rows and IDs are preserved when the table already has its owner column.
|
|
37
|
+
|
|
38
|
+
A nonempty pivot without an owner column needs an authoritative owner mapping.
|
|
39
|
+
Migration refuses it with the affected table and column in the error; it does
|
|
40
|
+
not assign zero or guess owners. Restore the missing IDs from a trustworthy
|
|
41
|
+
backup or repair the pivot explicitly before rerunning migration.
|
|
42
|
+
|
|
43
|
+
The default trait guarantees run unless the command receives `--no-auth`.
|
|
44
|
+
Trait migration failures return a nonzero command status. Applications should
|
|
45
|
+
let deployment stop on that failure instead of suppressing migration errors.
|
|
46
|
+
`migrateTraitTables()` returns `{ success: false, error }` for programmatic
|
|
47
|
+
callers. Automatic empty-pivot reconciliation is implemented for SQLite.
|
|
48
|
+
|
|
31
49
|
## 🧪 Testing
|
|
32
50
|
|
|
33
51
|
```bash
|
package/dist/migrations.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var {require}=import.meta;import{existsSync,mkdirSync,readdirSync,readFileSync,renameSync,rmSync,unlinkSync,writeFileSync}from"node:fs";import{isPackageMigration,stagePackageMigrations}from"./package-migrations";import{nextFreeMigrationOrdinal,nextMigrationOrdinal,reservedMigrationOrdinals,retiredMigrationNames}from"./migration-names";import{basename,dirname,isAbsolute,join,resolve}from"node:path";import{log as _log}from"@stacksjs/logging";const log={info:(...args)=>typeof _log?.info==="function"?_log.info(...args):console.log(...args),success:(msg)=>typeof _log?.success==="function"?_log.success(msg):console.log(msg),warn:(msg)=>typeof _log?.warn==="function"?_log.warn(msg):console.warn(msg),error:(...args)=>typeof _log?.error==="function"?_log.error(...args):console.error(...args),debug:(...args)=>typeof _log?.debug==="function"?_log.debug(...args):console.debug(...args)};import{err,handleError,ok}from"@stacksjs/error-handling";import{path}from"@stacksjs/path";import{defaultModelsPath}from"./seeder";import{createQueryBuilder,executeMigrationWithReport as qbExecuteMigrationWithReport,generateDiffOperations,generateMigration as qbGenerateMigration,resetDatabase as qbResetDatabase,config as qbConfig,saveMigrationSnapshot,setConfig}from"@stacksjs/query-builder";import{db,ensureDatabaseConfigLoaded,qbSnapshotDir,resetDatabaseConnection,resolvedDatabaseConfig}from"./utils";import{classifyConnectionError,createDatabase,describeTarget,manualCreateHint,probeTargetDatabase,resolveConnectionTarget}from"./ensure-database";import{declaredModelTables,resolveModelSources}from"./model-sources";import{applicableFrameworkRenames,applyFrameworkRenamesToDatabase,applyFrameworkRenamesToPlan}from"./framework-renames";import{findShadowedColumnDrops,shadowDropsAllowed,shadowedDropMessage}from"./shadowed-models";import{frameworkManagedColumns,withoutManagedColumnDrops,withoutManagedColumnDropSql}from"./managed-columns";import{acquireLibsqlMigrationLock,acquireMigrationLock}from"./migration-lock";import{auditMissingTables}from"./migration-ledger";import{relativeMigrationDirectory,resolveMigrationDirectory}from"./migration-path";import{ensureNotificationForeignKeys,migrateNotificationTables,notificationTablePreflight}from"./notification-tables";import{traitTableNames,traitTablePrerequisiteSql}from"./trait-tables";import{AUTH_TABLES}from"./auth-tables";import{enumTypesCreatedBy,guardedBlockStatements,SQL_IDENTIFIER,sqlStatementsOf}from"./sql-statements";import{indexShape}from"./index-shape";import{corpusUniqueIndexes,hasIndex,restorableMissingUniqueIndexes,restoreDroppedUniqueIndexes,uniqueIndexIsRestorable}from"./declared-unique-indexes";import{ensureUuidColumns}from"./uuid-columns";import{sqlHelpers}from"./sql-helpers";import{GENERATED_MIGRATION_MARKER,isGeneratedMigration}from"./generated-migration-marker";import{trackIgnoredGeneratedMigrations}from"./ignored-migrations";export{enumTypesCreatedBy,sqlStatementsOf}from"./sql-statements";import{isLibsqlDriver,isVitessSharded,toSqlIntrospectionDialect}from"./dialect";const dbConfig={get default(){return resolvedDatabaseConfig().driver},get connections(){const{connections}=resolvedDatabaseConfig();return{...connections,turso:connections.turso??{url:"",authToken:""}}}};function usesLibsql(){return isLibsqlDriver(getDriver())}function sqliteDatabasePath(){const configured=dbConfig.connections.sqlite.database||"stacks.db";return isAbsolute(configured)?configured:join(process.cwd(),configured)}function getDriver(){return generationDialect??attachedDriver()}function attachedDriver(){return dbConfig.default||"sqlite"}export const MIGRATION_DIALECTS=["sqlite","mysql","singlestore","vitess","postgres"];let generationDialect;function getDialect(){const driver=getDriver();if(driver==="sqlite"||driver==="mysql"||driver==="vitess"||driver==="postgres")return driver;if(driver==="turso")return"sqlite";if(driver==="singlestore")return"mysql";if(driver==="dynamodb")throw Error("[database] DB_CONNECTION=dynamodb is not compatible with the SQL migration runner. DynamoDB has no schema-migration concept - use the entity-style `dynamo.entity(...)` API from @stacksjs/database directly. To run SQL migrations, set DB_CONNECTION to one of: sqlite, turso, mysql, singlestore, vitess, postgres.");throw Error(`[database] Unknown DB_CONNECTION "${driver}". Allowed values: sqlite, turso, mysql, singlestore, vitess, postgres, dynamodb.`)}function getQbDialect(){return getDriver()==="singlestore"?"singlestore":getDialect()}function migrationDirectory(dialect=getQbDialect()){const committed=resolveMigrationDirectory(dialect,{configured:
|
|
1
|
+
var {require}=import.meta;import{existsSync,mkdirSync,readdirSync,readFileSync,renameSync,rmSync,unlinkSync,writeFileSync}from"node:fs";import{isPackageMigration,stagePackageMigrations}from"./package-migrations";import{nextFreeMigrationOrdinal,nextMigrationOrdinal,reservedMigrationOrdinals,retiredMigrationNames}from"./migration-names";import{basename,dirname,isAbsolute,join,resolve}from"node:path";import{log as _log}from"@stacksjs/logging";const log={info:(...args)=>typeof _log?.info==="function"?_log.info(...args):console.log(...args),success:(msg)=>typeof _log?.success==="function"?_log.success(msg):console.log(msg),warn:(msg)=>typeof _log?.warn==="function"?_log.warn(msg):console.warn(msg),error:(...args)=>typeof _log?.error==="function"?_log.error(...args):console.error(...args),debug:(...args)=>typeof _log?.debug==="function"?_log.debug(...args):console.debug(...args)};import{err,handleError,ok}from"@stacksjs/error-handling";import{path}from"@stacksjs/path";import{defaultModelsPath}from"./seeder";import{createQueryBuilder,executeMigrationWithReport as qbExecuteMigrationWithReport,generateDiffOperations,generateMigration as qbGenerateMigration,resetDatabase as qbResetDatabase,config as qbConfig,saveMigrationSnapshot,setConfig}from"@stacksjs/query-builder";import{db,ensureDatabaseConfigLoaded,qbMigrationDir,qbSnapshotDir,resetDatabaseConnection,resolvedDatabaseConfig}from"./utils";import{classifyConnectionError,createDatabase,describeTarget,manualCreateHint,probeTargetDatabase,resolveConnectionTarget}from"./ensure-database";import{declaredModelTables,resolveModelSources}from"./model-sources";import{applicableFrameworkRenames,applyFrameworkRenamesToDatabase,applyFrameworkRenamesToPlan}from"./framework-renames";import{findShadowedColumnDrops,shadowDropsAllowed,shadowedDropMessage}from"./shadowed-models";import{frameworkManagedColumns,withoutManagedColumnDrops,withoutManagedColumnDropSql}from"./managed-columns";import{acquireLibsqlMigrationLock,acquireMigrationLock}from"./migration-lock";import{auditMissingTables}from"./migration-ledger";import{relativeMigrationDirectory,resolveMigrationDirectory}from"./migration-path";import{ensureNotificationForeignKeys,migrateNotificationTables,notificationTablePreflight}from"./notification-tables";import{traitTableNames,traitTablePrerequisiteSql}from"./trait-tables";import{AUTH_TABLES}from"./auth-tables";import{enumTypesCreatedBy,guardedBlockStatements,SQL_IDENTIFIER,sqlStatementsOf}from"./sql-statements";import{indexShape}from"./index-shape";import{corpusUniqueIndexes,hasIndex,restorableMissingUniqueIndexes,restoreDroppedUniqueIndexes,uniqueIndexIsRestorable}from"./declared-unique-indexes";import{ensureUuidColumns}from"./uuid-columns";import{sqlHelpers}from"./sql-helpers";import{GENERATED_MIGRATION_MARKER,isGeneratedMigration}from"./generated-migration-marker";import{trackIgnoredGeneratedMigrations}from"./ignored-migrations";export{enumTypesCreatedBy,sqlStatementsOf}from"./sql-statements";import{isLibsqlDriver,isVitessSharded,toSqlIntrospectionDialect}from"./dialect";const dbConfig={get default(){return resolvedDatabaseConfig().driver},get connections(){const{connections}=resolvedDatabaseConfig();return{...connections,turso:connections.turso??{url:"",authToken:""}}}};function usesLibsql(){return isLibsqlDriver(getDriver())}function sqliteDatabasePath(){const configured=dbConfig.connections.sqlite.database||"stacks.db";return isAbsolute(configured)?configured:join(process.cwd(),configured)}function getDriver(){return generationDialect??attachedDriver()}function attachedDriver(){return dbConfig.default||"sqlite"}export const MIGRATION_DIALECTS=["sqlite","mysql","singlestore","vitess","postgres"];let generationDialect;function getDialect(){const driver=getDriver();if(driver==="sqlite"||driver==="mysql"||driver==="vitess"||driver==="postgres")return driver;if(driver==="turso")return"sqlite";if(driver==="singlestore")return"mysql";if(driver==="dynamodb")throw Error("[database] DB_CONNECTION=dynamodb is not compatible with the SQL migration runner. DynamoDB has no schema-migration concept - use the entity-style `dynamo.entity(...)` API from @stacksjs/database directly. To run SQL migrations, set DB_CONNECTION to one of: sqlite, turso, mysql, singlestore, vitess, postgres.");throw Error(`[database] Unknown DB_CONNECTION "${driver}". Allowed values: sqlite, turso, mysql, singlestore, vitess, postgres, dynamodb.`)}function getQbDialect(){return getDriver()==="singlestore"?"singlestore":getDialect()}function migrationDirectory(dialect=getQbDialect()){const committed=resolveMigrationDirectory(dialect,{configured:qbMigrationDir(),snapshotDir:qbSnapshotDir()});return runCorpus&&committed===runCorpus.committed?runCorpus.staged:committed}let runCorpus;function stageRunCorpus(dialect){const committed=migrationDirectory(dialect),staged=join(path.frameworkRuntimePath(),"migrations",`run-${process.pid}-${Date.now()}`);mkdirSync(staged,{recursive:!0});for(const file of listCorpusEntries(committed))writeFileSync(join(staged,file),readFileSync(join(committed,file)));runCorpus={committed,staged};return runCorpus}function releaseRunCorpus(run){if(!run)return;if(runCorpus?.staged===run.staged)runCorpus=void 0;try{rmSync(run.staged,{recursive:!0,force:!0})}catch{}}function listCorpusEntries(dir){try{return readdirSync(dir).filter((file)=>file.endsWith(".sql")||file.endsWith(".sql.unsupported"))}catch{return[]}}export async function activeMigrationDirectory(){await ensureDatabaseConfigLoaded();return migrationDirectory(getDialect())}function configureQueryBuilder(targetDialect=getQbDialect(),vitessSharded){const connectionConfig=dbConfig.connections[targetDialect],libsql=targetDialect==="sqlite"&&usesLibsql()?{url:dbConfig.connections.turso.url||void 0,authToken:dbConfig.connections.turso.authToken||void 0}:{url:void 0,authToken:void 0};if(targetDialect==="sqlite"&&usesLibsql()&&!libsql.url)throw Error("[database] DB_CONNECTION=turso needs TURSO_DATABASE_URL (libsql://<db>-<org>.turso.io, or http://127.0.0.1:8080 for `turso dev`).");setConfig({dialect:targetDialect,vitess:{sharded:isVitessSharded(vitessSharded??connectionConfig?.sharded)},verbose:!1,snapshotDir:qbSnapshotDir(),migrationDir:relativeMigrationDirectory(migrationDirectory(targetDialect)),database:{database:connectionConfig?.name||connectionConfig?.database||"stacks",host:connectionConfig?.host||"localhost",port:connectionConfig?.port||(targetDialect==="postgres"?5432:targetDialect==="vitess"?15306:targetDialect==="mysql"||targetDialect==="singlestore"?3306:0),username:connectionConfig?.username||"",password:connectionConfig?.password||"",...libsql}});resetDatabaseConnection()}export function prepareMigrationModelsDir(){const sources=resolveModelSources(),excludedTables=sources?.excludedTables??[];return{modelsDir:sources?.dir??path.userModelsPath(),skip:!sources,excludedTables,protectedTables:[...new Set([...excludedTables,...traitTableNames()])]}}function generatedStatementTables(statement){return statement.split(";").map((part)=>part.trim()).filter(Boolean).flatMap((part)=>{const table=rebuiltTable(part)??statementTable(part),index=/^\s*CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?\w+["`]?\s+ON\s+["`]?(\w+)["`]?/i.exec(part)?.[1],dropped=DROP_TABLE_RE.exec(part)?.[1];return[table,index,dropped].filter((name)=>Boolean(name)).map((name)=>name.toLowerCase())})}export function withoutRuntimeOwnedTableSql(statements,owned=AUTH_TABLES){const ownedSet=new Set(owned.map((table)=>table.toLowerCase())),removed=[];return{statements:statements.filter((statement)=>{if(!generatedStatementTables(statement).some((table)=>ownedSet.has(table)))return!0;removed.push(statement);return!1}),removed}}const DROP_TABLE_RE=/^\s*DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?["'`]?(\w+)["'`]?/i;export function withoutProtectedTableDropSql(statements,protectedTables,operations){if(protectedTables.length===0)return{statements,removed:[]};const excluded=new Set(protectedTables.map((table)=>table.toLowerCase())),normalize=(sql)=>sql.replace(/\s+/g," ").trim().replace(/;$/,""),dropped=new Set(operations.filter((op)=>op.kind==="drop_table"&&excluded.has(op.table.toLowerCase())).filter((op)=>Boolean(op.sql)).map((op)=>normalize(op.sql))),removed=[];return{statements:statements.filter((statement)=>{if(dropped.has(normalize(statement))){removed.push(statement);return!1}const match=statement.match(DROP_TABLE_RE);if(match?.[1]&&excluded.has(match[1].toLowerCase())){removed.push(statement);return!1}return!0}),removed}}export function orderPostgresColumnTypeChanges(sql){const lines=sql.split(`
|
|
2
2
|
`),output=[];for(const line of lines){const match=/^(\s*)ALTER\s+TABLE\s+("?[\w.]+"?)\s+ALTER\s+COLUMN\s+("?[\w]+"?)\s+TYPE\s/i.exec(line);if(match){const[,indent,table,column]=match,drop=`${indent}ALTER TABLE ${table} ALTER COLUMN ${column} DROP DEFAULT;`;if((output.length>0?output[output.length-1].trim():"")!==drop.trim())output.push(drop)}output.push(line)}return output.join(`
|
|
3
3
|
`)}export function guardPostgresEnumTypes(sql){return assertPostgresEnumMembers(wrapPostgresEnumTypes(sql))}function wrapPostgresEnumTypes(sql){return sql.replace(/CREATE\s+TYPE\s+("?[\w.]+"?)\s+AS\s+ENUM\s*\(([^)]*)\)/gi,(match,name,members,offset,whole)=>{if(/\bBEGIN\s*$/i.test(whole.slice(Math.max(0,offset-40),offset)))return match;return`DO $stacks$ BEGIN CREATE TYPE ${name} AS ENUM (${members}); EXCEPTION WHEN duplicate_object THEN null; END $stacks$`})}function assertPostgresEnumMembers(sql){return sql.replace(/DO \$stacks\$[\s\S]*?END \$stacks\$;?/g,(block)=>{const created=/CREATE\s+TYPE\s+("?[\w.]+"?)\s+AS\s+ENUM\s*\(([^)]*)\)/i.exec(block);if(!created)return block;const[,name,members]=created,missing=enumMembers(members).map((member)=>`ALTER TYPE ${name} ADD VALUE IF NOT EXISTS ${member};`).filter((statement)=>!sql.includes(statement));if(missing.length===0)return block;return`${block.endsWith(";")?block:`${block};`}
|
|
4
4
|
${missing.join(`
|
package/dist/schema-align.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export declare function planSchemaAlignment(desired: DesiredSchema, live: LiveAl
|
|
|
26
26
|
|
|
27
27
|
widenIntegers?: boolean
|
|
28
28
|
|
|
29
|
+
relaxConstraints?: boolean
|
|
30
|
+
|
|
29
31
|
typeSatisfies?: (declaredType: string, liveSqlType: string) => boolean
|
|
30
32
|
}): AlignmentPlan;
|
|
31
33
|
/** Read the parts of a Postgres schema the planner compares. */
|
|
@@ -50,6 +52,7 @@ export declare function evaluateAlignChecks(plan: AlignmentPlan, run: AlignSqlRu
|
|
|
50
52
|
export declare function alignSchema(options?: {
|
|
51
53
|
apply?: boolean
|
|
52
54
|
widenIntegers?: boolean
|
|
55
|
+
relaxConstraints?: boolean
|
|
53
56
|
dialect?: string
|
|
54
57
|
|
|
55
58
|
run?: AlignSqlRunner
|
|
@@ -140,7 +143,7 @@ export declare interface AlignResult {
|
|
|
140
143
|
}
|
|
141
144
|
/** Runs one SQL string and returns its rows (see `migration-ledger.ts`). */
|
|
142
145
|
export type AlignSqlRunner = (sql: string) => Promise<any[]>;
|
|
143
|
-
export type AlignActionKind = 'create-enum-type' | 'add-enum-value' | 'add-column' | 'widen-column' | 'rename-index' | 'create-index' | 'add-foreign-key';
|
|
146
|
+
export type AlignActionKind = 'create-enum-type' | 'add-enum-value' | 'add-column' | 'widen-column' | 'rename-index' | 'create-index' | 'add-foreign-key' | 'relax-nullability' | 'drop-polymorphic-foreign-key' | 'drop-conflicting-foreign-key';
|
|
144
147
|
export type AlignReportKind = | 'missing-table'
|
|
145
148
|
| 'missing-column'
|
|
146
149
|
| 'extra-column'
|
package/dist/schema-align.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{comparableIndexKey,indexShape,postgresIndexStatement}from"./index-shape";import{sqlStatementsOf}from"./sql-statements";const quote=(name)=>`"${name.replace(/"/g,'""')}"`,sqlString=(value)=>`'${value.replace(/'/g,"''")}'`;export function postgresIdentifier(name,max=63){if(name.length<=max)return name;let hash=2166136261;for(let i=0;i<name.length;i++){hash^=name.charCodeAt(i);hash=Math.imul(hash,16777619)>>>0}const suffix=`_${hash.toString(36).padStart(7,"0").slice(-7)}`;return name.slice(0,max-suffix.length)+suffix}function topLevelParts(body){const parts=[];let depth=0,current="",quoted=null;for(const char of body){if(quoted){current+=char;if(char===quoted)quoted=null;continue}if(char==="'"||char==='"'){quoted=char;current+=char;continue}if(char==="(")depth++;if(char===")")depth--;if(char===","&&depth===0){parts.push(current);current="";continue}current+=char}parts.push(current);return parts.map((part)=>part.trim()).filter(Boolean)}export function columnDefinitionsOf(createStatement){const definitions=new Map,body=createStatement.match(/\(([\s\S]*)\)\s*;?\s*$/)?.[1];if(!body)return definitions;const constraint=/^\s*(?:PRIMARY\s+KEY|FOREIGN\s+KEY|UNIQUE|CHECK|CONSTRAINT|KEY|INDEX)\b/i;for(const part of topLevelParts(body)){if(constraint.test(part))continue;const name=part.match(/^["'`]?(\w+)["'`]?/)?.[1];if(name)definitions.set(name.toLowerCase(),part)}return definitions}function indexColumnsAndPredicate(statement){const open=/\bON\s+\S+\s*\(/i.exec(statement);if(!open)return;const start=open.index+open[0].length;let depth=1,end=start;for(;end<statement.length&&depth>0;end++)if(statement[end]==="(")depth++;else if(statement[end]===")")depth--;if(depth>0)return;return[statement,statement.slice(start,end-1),statement.slice(end).trim()]}export function desiredSchemaFrom(plan,statements){const creates=new Map,indexes=[],enums=new Map,visit=(statement)=>{const table=/^\s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`]?(\w+)["'`]?/i.exec(statement)?.[1];if(table){creates.set(table.toLowerCase(),statement);return}const enumType=/^\s*CREATE\s+TYPE\s+["]?([\w$]+)["]?\s+AS\s+ENUM\s*\(([\s\S]*)\)\s*;?\s*$/i.exec(statement);if(enumType?.[1]){const labels=[...(enumType[2]??"").matchAll(/'((?:[^']|'')*)'/g)].map((m)=>(m[1]??"").replace(/''/g,"'"));enums.set(enumType[1].toLowerCase(),labels);return}const guarded=/^\s*DO\s+\$(\w*)\$\s*BEGIN\s+([\s\S]*?);\s*EXCEPTION\b/i.exec(statement);if(guarded?.[2]){visit(guarded[2]);return}const shape=indexShape(statement),key=comparableIndexKey(statement);if(shape&&key){const head=indexColumnsAndPredicate(statement),parts=topLevelParts(head?.[1]??""),plain=parts.every((part)=>/^["`]?\w+["`]?$/.test(part));indexes.push({name:shape.name,table:shape.table,unique:/^\s*CREATE\s+UNIQUE\b/i.test(statement),...plain?{columns:parts.map((part)=>part.replace(/["`]/g,"").toLowerCase())}:{},key,where:(head?.[2]??"").replace(/;\s*$/,"").trim(),statement:statement.trim().replace(/;\s*$/,"")})}};for(const statement of statements)for(const part of sqlStatementsOf(statement.trim().endsWith(";")?statement:`${statement};`))visit(part);const tables=new Map;for(const table of plan.tables){const name=table.table.toLowerCase(),definitions=creates.has(name)?columnDefinitionsOf(creates.get(name)):new Map;tables.set(name,table.columns.map((column)=>({name:column.name.toLowerCase(),type:column.type,...column.maxLength!==void 0?{maxLength:column.maxLength}:{},isPrimaryKey:column.isPrimaryKey,isNullable:column.isNullable,hasDefault:column.hasDefault,...column.defaultValue!==void 0?{defaultValue:column.defaultValue}:{},...definitions.get(column.name.toLowerCase())?{definition:definitions.get(column.name.toLowerCase())}:{},...column.references?{references:{...column.references}}:{}})))}return{tables,indexes,enums}}const INTEGER_RANK={smallint:1,integer:2,bigint:3};function declaredIntegerRank(type){if(type==="integer")return INTEGER_RANK.integer;if(type==="bigint")return INTEGER_RANK.bigint;return}function liveIntegerRank(column){return INTEGER_RANK[column.dataType]}export function comparableDefault(value){if(value===void 0||value===null)return;let text=String(value).trim();if(/^nextval\(/i.test(text))return;if(/\bnow\(\)|\b(?:current_timestamp|utc_timestamp|localtimestamp)\b/i.test(text))return"clock";text=text.replace(/::\s*(?:"[^"]+"|[a-z_]\w*)(?:\s+varying|\s+precision|\s+with(?:out)?\s+time\s+zone)?(?:\[\])?/gi,"");while(/^\(.*\)$/s.test(text))text=text.slice(1,-1).trim();text=text.replace(/^'(.*)'$/s,"$1").replace(/''/g,"'");return text.toLowerCase()}function declaredDefault(column){if(!column.hasDefault||column.defaultValue===void 0||column.defaultValue===null)return;const value=column.defaultValue;if(value instanceof Date)return comparableDefault(value.toISOString());return comparableDefault(typeof value==="string"?value:String(value))}function foreignKeyAction(action){return(action??"no action").toUpperCase()}const PG_ACTION={a:"NO ACTION",r:"RESTRICT",c:"CASCADE",n:"SET NULL",d:"SET DEFAULT"};export function planSchemaAlignment(desired,live,options={}){const actions=[],reports=[],added=new Set;for(const[type,labels]of desired.enums){const existing=live.enums.get(type);if(!existing){actions.push({kind:"create-enum-type",table:type,name:type,detail:`create enum type ${type} (${labels.join(", ")})`,sql:`CREATE TYPE ${quote(type)} AS ENUM (${labels.map(sqlString).join(", ")})`});continue}for(const label of labels.filter((label)=>!existing.includes(label)))actions.push({kind:"add-enum-value",table:type,name:label,detail:`add '${label}' to enum type ${type}`,sql:`ALTER TYPE ${quote(type)} ADD VALUE IF NOT EXISTS ${sqlString(label)}`});const extra=existing.filter((label)=>!labels.includes(label));if(extra.length>0)reports.push({kind:"enum",table:type,detail:`enum type ${type} has label(s) the model does not declare: ${extra.join(", ")}`})}for(const[table,columns]of desired.tables){const liveColumns=live.tables.get(table);if(!liveColumns){reports.push({kind:"missing-table",table,detail:`table ${table} does not exist - \`buddy migrate\` creates it`});continue}const declared=new Set(columns.map((column)=>column.name));for(const name of liveColumns.keys())if(!declared.has(name))reports.push({kind:"extra-column",table,name,detail:`${table}.${name} exists but no model declares it`});for(const column of columns){const current=liveColumns.get(column.name);if(!current){if(!column.definition||/\bPRIMARY\s+KEY\b|\bUNIQUE\b/i.test(column.definition)){reports.push({kind:"missing-column",table,name:column.name,detail:`${table}.${column.name} is missing and cannot be added in place`});continue}const inlineReference=/\bREFERENCES\s+"([^"]+)"/i.exec(column.definition)?.[1]?.toLowerCase();if(inlineReference&&!live.tables.has(inlineReference)){reports.push({kind:"foreign-key",table,name:column.name,detail:`${table}.${column.name} references ${inlineReference}, which does not exist - \`buddy migrate\` creates that table`});continue}const strict=/\bNOT\s+NULL\b/i.test(column.definition)&&!/\bDEFAULT\b/i.test(column.definition),populated=live.populated?.has(table)??!0,definition=strict&&populated?column.definition.replace(/\s*\bNOT\s+NULL\b/i,""):column.definition;actions.push({kind:"add-column",table,name:column.name,detail:`add ${table}.${column.name}${definition!==column.definition?" (nullable: the table has rows and the column has no default)":""}`,sql:`ALTER TABLE ${quote(table)} ADD COLUMN IF NOT EXISTS ${definition}`});added.add(`${table}.${column.name}`);if(definition!==column.definition)reports.push({kind:"nullability",table,name:column.name,detail:`${table}.${column.name} is added nullable; backfill it, then make it NOT NULL`});continue}if(column.isPrimaryKey)continue;const liveSql=current.maxLength?`${current.dataType}(${current.maxLength})`:current.dataType;if(column.type==="string"||column.type==="text"){const wanted=column.type==="text"?void 0:column.maxLength??255,isVarchar=current.dataType==="character varying",isText=current.dataType==="text"||isVarchar&&!current.maxLength;if(isVarchar&¤t.maxLength&&(wanted===void 0||wanted>current.maxLength)){const to=wanted===void 0?"text":`varchar(${wanted})`;actions.push({kind:"widen-column",table,name:column.name,detail:`widen ${table}.${column.name} from varchar(${current.maxLength}) to ${to}`,sql:`ALTER TABLE ${quote(table)} ALTER COLUMN ${quote(column.name)} TYPE ${to}`})}else if(isVarchar&¤t.maxLength&&wanted!==void 0&&wanted<current.maxLength)reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is varchar(${current.maxLength}); the model declares varchar(${wanted}) - narrowing could truncate data`});else if(isText&&wanted!==void 0)reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is unbounded text; the model declares varchar(${wanted}) - narrowing could truncate data`});else if(!isVarchar&&!isText&¤t.dataType!=="USER-DEFINED")reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is ${liveSql}; the model declares ${column.type==="text"?"text":`varchar(${wanted})`}`})}else if(declaredIntegerRank(column.type)!==void 0&&liveIntegerRank(current)!==void 0){const want=declaredIntegerRank(column.type),have=liveIntegerRank(current);if(want>have)if(options.widenIntegers)actions.push({kind:"widen-column",table,name:column.name,detail:`widen ${table}.${column.name} from ${current.dataType} to bigint (rewrites the table)`,sql:`ALTER TABLE ${quote(table)} ALTER COLUMN ${quote(column.name)} TYPE bigint`});else reports.push({kind:"optional-widening",table,name:column.name,detail:`${table}.${column.name} is ${current.dataType}; the model declares bigint - pass --widen-integers to convert it (rewrites the table)`});else if(want<have)reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is ${current.dataType}; the model declares ${column.type} - narrowing could overflow`})}else if(column.type!=="enum"&&options.typeSatisfies&&!options.typeSatisfies(column.type,current.dataType))reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is ${liveSql}; the model declares ${column.type}`});const wantNullable=column.isNullable;if(wantNullable!==current.nullable)reports.push({kind:"nullability",table,name:column.name,detail:wantNullable?`${table}.${column.name} is NOT NULL; the model allows NULL`:`${table}.${column.name} allows NULL; the model declares NOT NULL`});const want=declaredDefault(column),have=comparableDefault(current.default);if((want??"")!==(have??"")&&!(want===void 0&&have==="null"))reports.push({kind:"default",table,name:column.name,detail:`${table}.${column.name} defaults to ${current.default??"nothing"}; the model declares ${want===void 0?"no default":String(column.defaultValue)}`})}}const desiredNames=new Set(desired.indexes.map((index)=>index.name)),liveByName=new Map(live.indexes.map((index)=>[index.name,index])),claimed=new Set,renames=[],creates=[];for(const index of desired.indexes){const liveColumns=live.tables.get(index.table);if(!liveColumns)continue;const existing=liveByName.get(index.name);if(existing){claimed.add(existing.name);if(existing.table!==index.table||existing.key&&existing.key!==index.key)reports.push({kind:"index-definition",table:index.table,name:index.name,detail:`index ${index.name} exists but differs from the model (${existing.definition}) - drop and recreate it by hand if the model is right`});continue}const twins=live.indexes.filter((candidate)=>candidate.table===index.table&&!candidate.primary&&candidate.key===index.key&&!desiredNames.has(candidate.name)&&!claimed.has(candidate.name));if(twins.length>0){const[twin,...extra]=[...twins].sort((a,b)=>Number(b.name.includes(index.name))-Number(a.name.includes(index.name)));claimed.add(twin.name);if(live.names.has(index.name))reports.push({kind:"name-conflict",table:index.table,name:index.name,detail:`cannot rename ${twin.name} to ${index.name}: that name is taken by another object`});else renames.push({kind:"rename-index",table:index.table,name:index.name,detail:`rename index ${twin.name} to ${index.name}${twin.constraint?" (and the constraint it backs)":""}`,sql:`ALTER INDEX ${quote(twin.name)} RENAME TO ${quote(index.name)}`});for(const duplicate of extra){claimed.add(duplicate.name);reports.push({kind:"duplicate-index",table:index.table,name:duplicate.name,detail:`${duplicate.name} duplicates ${index.name} - drop it by hand once nothing depends on it`})}continue}const missingColumn=index.columns?.find((column)=>!liveColumns.has(column)&&!added.has(`${index.table}.${column}`));if(missingColumn){reports.push({kind:"missing-column",table:index.table,name:missingColumn,detail:`index ${index.name} needs ${index.table}.${missingColumn}, which cannot be added here`});continue}const statement=index.statement.replace(/^\s*CREATE\s+(UNIQUE\s+)?INDEX\s+(?!IF\s+NOT\s+EXISTS)/i,(_m,unique)=>`CREATE ${unique??""}INDEX IF NOT EXISTS `),action={kind:"create-index",table:index.table,name:index.name,detail:`create ${index.unique?"unique ":""}index ${index.name}`,sql:statement};if(index.unique&&index.columns){const columns=index.columns.map(quote).join(", "),where=[...index.columns.map((column)=>`${quote(column)} IS NOT NULL`),...index.where?[`(${index.where.replace(/^WHERE\s+/i,"")})`]:[]].join(" AND ");action.check={kind:"duplicates",sql:`SELECT COUNT(*) AS n FROM (SELECT 1 FROM ${quote(index.table)} WHERE ${where} GROUP BY ${columns} HAVING COUNT(*) > 1) AS duplicates`,dependsOn:[...index.columns]}}creates.push(action)}const foreignKeys=[];for(const[table,columns]of desired.tables){const liveColumns=live.tables.get(table);if(!liveColumns)continue;for(const column of columns){const ref=column.references;if(!ref)continue;const refTable=ref.table.toLowerCase(),refColumn=ref.column.toLowerCase();if(added.has(`${table}.${column.name}`)||!liveColumns.has(column.name))continue;const present=live.foreignKeys.find((fk)=>fk.table===table&&fk.column===column.name&&fk.refTable===refTable&&fk.refColumn===refColumn);if(present){const wantDelete=foreignKeyAction(ref.onDelete),wantUpdate=foreignKeyAction(ref.onUpdate);if(present.onDelete!==wantDelete||present.onUpdate!==wantUpdate)reports.push({kind:"foreign-key",table,name:present.name,detail:`${present.name} is ON DELETE ${present.onDelete} ON UPDATE ${present.onUpdate}; the model declares ON DELETE ${wantDelete} ON UPDATE ${wantUpdate}`});continue}if(!live.tables.get(refTable)?.has(refColumn)){reports.push({kind:"foreign-key",table,name:column.name,detail:`${table}.${column.name} references ${refTable}.${refColumn}, which does not exist`});continue}const name=postgresIdentifier(ref.constraintNames?.[0]??`${table}_${column.name}_fkey`);if(live.names.has(name)){reports.push({kind:"name-conflict",table,name,detail:`cannot add the foreign key for ${table}.${column.name} as ${name}: that name is taken`});continue}const actionsSql=[ref.onDelete?` ON DELETE ${foreignKeyAction(ref.onDelete)}`:"",ref.onUpdate?` ON UPDATE ${foreignKeyAction(ref.onUpdate)}`:""].join("");foreignKeys.push({kind:"add-foreign-key",table,name,detail:`add foreign key ${table}.${column.name} -> ${refTable}.${refColumn}`,sql:`ALTER TABLE ${quote(table)} ADD CONSTRAINT ${quote(name)} FOREIGN KEY (${quote(column.name)}) REFERENCES ${quote(refTable)} (${quote(refColumn)})${actionsSql}`,check:{kind:"orphans",sql:`SELECT COUNT(*) AS n FROM ${quote(table)} AS child WHERE child.${quote(column.name)} IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${quote(refTable)} AS parent WHERE parent.${quote(refColumn)} = child.${quote(column.name)})`,dependsOn:[column.name]}})}}return{actions:[...actions,...renames,...creates,...foreignKeys],reports,refused:[]}}function rows(result){return Array.isArray(result)?result:[]}function text(row,key){const value=row?.[key];return value===null||value===void 0?"":String(value)}export async function readPostgresAlignSchema(run){const tables=new Map;for(const row of rows(await run("SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema() AND table_type = 'BASE TABLE'")))tables.set(text(row,"table_name").toLowerCase(),new Map);for(const row of rows(await run("SELECT table_name, column_name, data_type, udt_name, character_maximum_length, is_nullable, column_default FROM information_schema.columns WHERE table_schema = current_schema()"))){const columns=tables.get(text(row,"table_name").toLowerCase());if(!columns)continue;const length=Number(row.character_maximum_length);columns.set(text(row,"column_name").toLowerCase(),{name:text(row,"column_name").toLowerCase(),dataType:text(row,"data_type").toLowerCase()==="user-defined"?"USER-DEFINED":text(row,"data_type").toLowerCase(),udtName:text(row,"udt_name").toLowerCase(),...Number.isFinite(length)&&length>0?{maxLength:length}:{},nullable:text(row,"is_nullable").toUpperCase()==="YES",...row.column_default!==null&&row.column_default!==void 0?{default:String(row.column_default)}:{}})}const indexes=[];for(const row of rows(await run(`
|
|
1
|
+
import{comparableIndexKey,indexShape,postgresIndexStatement}from"./index-shape";import{sqlStatementsOf}from"./sql-statements";const quote=(name)=>`"${name.replace(/"/g,'""')}"`,sqlString=(value)=>`'${value.replace(/'/g,"''")}'`;export function postgresIdentifier(name,max=63){if(name.length<=max)return name;let hash=2166136261;for(let i=0;i<name.length;i++){hash^=name.charCodeAt(i);hash=Math.imul(hash,16777619)>>>0}const suffix=`_${hash.toString(36).padStart(7,"0").slice(-7)}`;return name.slice(0,max-suffix.length)+suffix}function topLevelParts(body){const parts=[];let depth=0,current="",quoted=null;for(const char of body){if(quoted){current+=char;if(char===quoted)quoted=null;continue}if(char==="'"||char==='"'){quoted=char;current+=char;continue}if(char==="(")depth++;if(char===")")depth--;if(char===","&&depth===0){parts.push(current);current="";continue}current+=char}parts.push(current);return parts.map((part)=>part.trim()).filter(Boolean)}export function columnDefinitionsOf(createStatement){const definitions=new Map,body=createStatement.match(/\(([\s\S]*)\)\s*;?\s*$/)?.[1];if(!body)return definitions;const constraint=/^\s*(?:PRIMARY\s+KEY|FOREIGN\s+KEY|UNIQUE|CHECK|CONSTRAINT|KEY|INDEX)\b/i;for(const part of topLevelParts(body)){if(constraint.test(part))continue;const name=part.match(/^["'`]?(\w+)["'`]?/)?.[1];if(name)definitions.set(name.toLowerCase(),part)}return definitions}function indexColumnsAndPredicate(statement){const open=/\bON\s+\S+\s*\(/i.exec(statement);if(!open)return;const start=open.index+open[0].length;let depth=1,end=start;for(;end<statement.length&&depth>0;end++)if(statement[end]==="(")depth++;else if(statement[end]===")")depth--;if(depth>0)return;return[statement,statement.slice(start,end-1),statement.slice(end).trim()]}export function desiredSchemaFrom(plan,statements){const creates=new Map,indexes=[],enums=new Map,visit=(statement)=>{const table=/^\s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`]?(\w+)["'`]?/i.exec(statement)?.[1];if(table){creates.set(table.toLowerCase(),statement);return}const enumType=/^\s*CREATE\s+TYPE\s+["]?([\w$]+)["]?\s+AS\s+ENUM\s*\(([\s\S]*)\)\s*;?\s*$/i.exec(statement);if(enumType?.[1]){const labels=[...(enumType[2]??"").matchAll(/'((?:[^']|'')*)'/g)].map((m)=>(m[1]??"").replace(/''/g,"'"));enums.set(enumType[1].toLowerCase(),labels);return}const guarded=/^\s*DO\s+\$(\w*)\$\s*BEGIN\s+([\s\S]*?);\s*EXCEPTION\b/i.exec(statement);if(guarded?.[2]){visit(guarded[2]);return}const shape=indexShape(statement),key=comparableIndexKey(statement);if(shape&&key){const head=indexColumnsAndPredicate(statement),parts=topLevelParts(head?.[1]??""),plain=parts.every((part)=>/^["`]?\w+["`]?$/.test(part));indexes.push({name:shape.name,table:shape.table,unique:/^\s*CREATE\s+UNIQUE\b/i.test(statement),...plain?{columns:parts.map((part)=>part.replace(/["`]/g,"").toLowerCase())}:{},key,where:(head?.[2]??"").replace(/;\s*$/,"").trim(),statement:statement.trim().replace(/;\s*$/,"")})}};for(const statement of statements)for(const part of sqlStatementsOf(statement.trim().endsWith(";")?statement:`${statement};`))visit(part);const tables=new Map;for(const table of plan.tables){const name=table.table.toLowerCase(),definitions=creates.has(name)?columnDefinitionsOf(creates.get(name)):new Map;tables.set(name,table.columns.map((column)=>({name:column.name.toLowerCase(),type:column.type,...column.maxLength!==void 0?{maxLength:column.maxLength}:{},isPrimaryKey:column.isPrimaryKey,isNullable:column.isNullable,hasDefault:column.hasDefault,...column.defaultValue!==void 0?{defaultValue:column.defaultValue}:{},...definitions.get(column.name.toLowerCase())?{definition:definitions.get(column.name.toLowerCase())}:{},...column.references?{references:{...column.references}}:{}})))}return{tables,indexes,enums}}const INTEGER_RANK={smallint:1,integer:2,bigint:3};function declaredIntegerRank(type){if(type==="integer")return INTEGER_RANK.integer;if(type==="bigint")return INTEGER_RANK.bigint;return}function liveIntegerRank(column){return INTEGER_RANK[column.dataType]}export function comparableDefault(value){if(value===void 0||value===null)return;let text=String(value).trim();if(/^nextval\(/i.test(text))return;if(/\bnow\(\)|\b(?:current_timestamp|utc_timestamp|localtimestamp)\b/i.test(text))return"clock";text=text.replace(/::\s*(?:"[^"]+"|[a-z_]\w*)(?:\s+varying|\s+precision|\s+with(?:out)?\s+time\s+zone)?(?:\[\])?/gi,"");while(/^\(.*\)$/s.test(text))text=text.slice(1,-1).trim();text=text.replace(/^'(.*)'$/s,"$1").replace(/''/g,"'");return text.toLowerCase()}function declaredDefault(column){if(!column.hasDefault||column.defaultValue===void 0||column.defaultValue===null)return;const value=column.defaultValue;if(value instanceof Date)return comparableDefault(value.toISOString());return comparableDefault(typeof value==="string"?value:String(value))}function foreignKeyAction(action){return(action??"no action").toUpperCase()}const PG_ACTION={a:"NO ACTION",r:"RESTRICT",c:"CASCADE",n:"SET NULL",d:"SET DEFAULT"};export function planSchemaAlignment(desired,live,options={}){const actions=[],reports=[],added=new Set;for(const[type,labels]of desired.enums){const existing=live.enums.get(type);if(!existing){actions.push({kind:"create-enum-type",table:type,name:type,detail:`create enum type ${type} (${labels.join(", ")})`,sql:`CREATE TYPE ${quote(type)} AS ENUM (${labels.map(sqlString).join(", ")})`});continue}for(const label of labels.filter((label)=>!existing.includes(label)))actions.push({kind:"add-enum-value",table:type,name:label,detail:`add '${label}' to enum type ${type}`,sql:`ALTER TYPE ${quote(type)} ADD VALUE IF NOT EXISTS ${sqlString(label)}`});const extra=existing.filter((label)=>!labels.includes(label));if(extra.length>0)reports.push({kind:"enum",table:type,detail:`enum type ${type} has label(s) the model does not declare: ${extra.join(", ")}`})}for(const[table,columns]of desired.tables){const liveColumns=live.tables.get(table);if(!liveColumns){reports.push({kind:"missing-table",table,detail:`table ${table} does not exist - \`buddy migrate\` creates it`});continue}const declared=new Set(columns.map((column)=>column.name));for(const name of liveColumns.keys())if(!declared.has(name))reports.push({kind:"extra-column",table,name,detail:`${table}.${name} exists but no model declares it`});for(const column of columns){const current=liveColumns.get(column.name);if(!current){if(!column.definition||/\bPRIMARY\s+KEY\b|\bUNIQUE\b/i.test(column.definition)){reports.push({kind:"missing-column",table,name:column.name,detail:`${table}.${column.name} is missing and cannot be added in place`});continue}const inlineReference=/\bREFERENCES\s+"([^"]+)"/i.exec(column.definition)?.[1]?.toLowerCase();if(inlineReference&&!live.tables.has(inlineReference)){reports.push({kind:"foreign-key",table,name:column.name,detail:`${table}.${column.name} references ${inlineReference}, which does not exist - \`buddy migrate\` creates that table`});continue}const strict=/\bNOT\s+NULL\b/i.test(column.definition)&&!/\bDEFAULT\b/i.test(column.definition),populated=live.populated?.has(table)??!0,definition=strict&&populated?column.definition.replace(/\s*\bNOT\s+NULL\b/i,""):column.definition;actions.push({kind:"add-column",table,name:column.name,detail:`add ${table}.${column.name}${definition!==column.definition?" (nullable: the table has rows and the column has no default)":""}`,sql:`ALTER TABLE ${quote(table)} ADD COLUMN IF NOT EXISTS ${definition}`});added.add(`${table}.${column.name}`);if(definition!==column.definition)reports.push({kind:"nullability",table,name:column.name,detail:`${table}.${column.name} is added nullable; backfill it, then make it NOT NULL`});continue}if(column.isPrimaryKey)continue;const liveSql=current.maxLength?`${current.dataType}(${current.maxLength})`:current.dataType;if(column.type==="string"||column.type==="text"){const wanted=column.type==="text"?void 0:column.maxLength??255,isVarchar=current.dataType==="character varying",isText=current.dataType==="text"||isVarchar&&!current.maxLength;if(isVarchar&¤t.maxLength&&(wanted===void 0||wanted>current.maxLength)){const to=wanted===void 0?"text":`varchar(${wanted})`;actions.push({kind:"widen-column",table,name:column.name,detail:`widen ${table}.${column.name} from varchar(${current.maxLength}) to ${to}`,sql:`ALTER TABLE ${quote(table)} ALTER COLUMN ${quote(column.name)} TYPE ${to}`})}else if(isVarchar&¤t.maxLength&&wanted!==void 0&&wanted<current.maxLength)reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is varchar(${current.maxLength}); the model declares varchar(${wanted}) - narrowing could truncate data`});else if(isText&&wanted!==void 0)reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is unbounded text; the model declares varchar(${wanted}) - narrowing could truncate data`});else if(!isVarchar&&!isText&¤t.dataType!=="USER-DEFINED")reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is ${liveSql}; the model declares ${column.type==="text"?"text":`varchar(${wanted})`}`})}else if(declaredIntegerRank(column.type)!==void 0&&liveIntegerRank(current)!==void 0){const want=declaredIntegerRank(column.type),have=liveIntegerRank(current);if(want>have)if(options.widenIntegers)actions.push({kind:"widen-column",table,name:column.name,detail:`widen ${table}.${column.name} from ${current.dataType} to bigint (rewrites the table)`,sql:`ALTER TABLE ${quote(table)} ALTER COLUMN ${quote(column.name)} TYPE bigint`});else reports.push({kind:"optional-widening",table,name:column.name,detail:`${table}.${column.name} is ${current.dataType}; the model declares bigint - pass --widen-integers to convert it (rewrites the table)`});else if(want<have)reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is ${current.dataType}; the model declares ${column.type} - narrowing could overflow`})}else if(column.type!=="enum"&&options.typeSatisfies&&!options.typeSatisfies(column.type,current.dataType))reports.push({kind:"type",table,name:column.name,detail:`${table}.${column.name} is ${liveSql}; the model declares ${column.type}`});const wantNullable=column.isNullable;if(wantNullable!==current.nullable)if(options.relaxConstraints&&wantNullable&&!column.isPrimaryKey)actions.push({kind:"relax-nullability",table,name:column.name,detail:`allow NULL in ${table}.${column.name}, as the model declares`,sql:`ALTER TABLE ${quote(table)} ALTER COLUMN ${quote(column.name)} DROP NOT NULL`});else reports.push({kind:"nullability",table,name:column.name,detail:wantNullable?`${table}.${column.name} is NOT NULL; the model allows NULL`:`${table}.${column.name} allows NULL; the model declares NOT NULL`});const want=declaredDefault(column),have=comparableDefault(current.default);if((want??"")!==(have??"")&&!(want===void 0&&have==="null"))reports.push({kind:"default",table,name:column.name,detail:`${table}.${column.name} defaults to ${current.default??"nothing"}; the model declares ${want===void 0?"no default":String(column.defaultValue)}`})}}const desiredNames=new Set(desired.indexes.map((index)=>index.name)),liveByName=new Map(live.indexes.map((index)=>[index.name,index])),claimed=new Set,renames=[],creates=[];for(const index of desired.indexes){const liveColumns=live.tables.get(index.table);if(!liveColumns)continue;const existing=liveByName.get(index.name);if(existing){claimed.add(existing.name);if(existing.table!==index.table||existing.key&&existing.key!==index.key)reports.push({kind:"index-definition",table:index.table,name:index.name,detail:`index ${index.name} exists but differs from the model (${existing.definition}) - drop and recreate it by hand if the model is right`});continue}const twins=live.indexes.filter((candidate)=>candidate.table===index.table&&!candidate.primary&&candidate.key===index.key&&!desiredNames.has(candidate.name)&&!claimed.has(candidate.name));if(twins.length>0){const[twin,...extra]=[...twins].sort((a,b)=>Number(b.name.includes(index.name))-Number(a.name.includes(index.name)));claimed.add(twin.name);if(live.names.has(index.name))reports.push({kind:"name-conflict",table:index.table,name:index.name,detail:`cannot rename ${twin.name} to ${index.name}: that name is taken by another object`});else renames.push({kind:"rename-index",table:index.table,name:index.name,detail:`rename index ${twin.name} to ${index.name}${twin.constraint?" (and the constraint it backs)":""}`,sql:`ALTER INDEX ${quote(twin.name)} RENAME TO ${quote(index.name)}`});for(const duplicate of extra){claimed.add(duplicate.name);reports.push({kind:"duplicate-index",table:index.table,name:duplicate.name,detail:`${duplicate.name} duplicates ${index.name} - drop it by hand once nothing depends on it`})}continue}const missingColumn=index.columns?.find((column)=>!liveColumns.has(column)&&!added.has(`${index.table}.${column}`));if(missingColumn){reports.push({kind:"missing-column",table:index.table,name:missingColumn,detail:`index ${index.name} needs ${index.table}.${missingColumn}, which cannot be added here`});continue}const statement=index.statement.replace(/^\s*CREATE\s+(UNIQUE\s+)?INDEX\s+(?!IF\s+NOT\s+EXISTS)/i,(_m,unique)=>`CREATE ${unique??""}INDEX IF NOT EXISTS `),action={kind:"create-index",table:index.table,name:index.name,detail:`create ${index.unique?"unique ":""}index ${index.name}`,sql:statement};if(index.unique&&index.columns){const columns=index.columns.map(quote).join(", "),where=[...index.columns.map((column)=>`${quote(column)} IS NOT NULL`),...index.where?[`(${index.where.replace(/^WHERE\s+/i,"")})`]:[]].join(" AND ");action.check={kind:"duplicates",sql:`SELECT COUNT(*) AS n FROM (SELECT 1 FROM ${quote(index.table)} WHERE ${where} GROUP BY ${columns} HAVING COUNT(*) > 1) AS duplicates`,dependsOn:[...index.columns]}}creates.push(action)}const obsolete=new Set;for(const foreignKey of live.foreignKeys){const columns=desired.tables.get(foreignKey.table),column=columns?.find((value)=>value.name===foreignKey.column),typeColumn=foreignKey.column.replace(/_id$/,"_type"),key=`${foreignKey.table}:${foreignKey.name}`;if(!column||column.references||typeColumn===foreignKey.column||!columns?.some((value)=>value.name===typeColumn)||obsolete.has(key))continue;obsolete.add(key);if(options.relaxConstraints)actions.push({kind:"drop-polymorphic-foreign-key",table:foreignKey.table,name:foreignKey.name,detail:`remove ${foreignKey.name}: ${foreignKey.column} has a polymorphic type column`,sql:`ALTER TABLE ${quote(foreignKey.table)} DROP CONSTRAINT ${quote(foreignKey.name)}`});else reports.push({kind:"foreign-key",table:foreignKey.table,name:foreignKey.name,detail:`${foreignKey.name} constrains a polymorphic reference to one table; use --relax-constraints to remove it`})}const foreignKeys=[];for(const[table,columns]of desired.tables){const liveColumns=live.tables.get(table);if(!liveColumns)continue;for(const column of columns){const ref=column.references;if(!ref)continue;const refTable=ref.table.toLowerCase(),refColumn=ref.column.toLowerCase();if(added.has(`${table}.${column.name}`)||!liveColumns.has(column.name))continue;const present=live.foreignKeys.find((fk)=>fk.table===table&&fk.column===column.name&&fk.refTable===refTable&&fk.refColumn===refColumn),conflicts=live.foreignKeys.filter((fk)=>fk.table===table&&fk.column===column.name&&(fk.refTable!==refTable||fk.refColumn!==refColumn));for(const conflict of conflicts)if(present&&options.relaxConstraints)actions.push({kind:"drop-conflicting-foreign-key",table,name:conflict.name,detail:`remove ${conflict.name}: ${table}.${column.name} already references the model's ${refTable}.${refColumn}`,sql:`ALTER TABLE ${quote(table)} DROP CONSTRAINT ${quote(conflict.name)}`});else reports.push({kind:"foreign-key",table,name:conflict.name,detail:`${conflict.name} references ${conflict.refTable}.${conflict.refColumn} instead of ${refTable}.${refColumn}; removal requires --relax-constraints and an enforced model reference`});if(present){const wantDelete=foreignKeyAction(ref.onDelete),wantUpdate=foreignKeyAction(ref.onUpdate);if(present.onDelete!==wantDelete||present.onUpdate!==wantUpdate)reports.push({kind:"foreign-key",table,name:present.name,detail:`${present.name} is ON DELETE ${present.onDelete} ON UPDATE ${present.onUpdate}; the model declares ON DELETE ${wantDelete} ON UPDATE ${wantUpdate}`});continue}if(!live.tables.get(refTable)?.has(refColumn)){reports.push({kind:"foreign-key",table,name:column.name,detail:`${table}.${column.name} references ${refTable}.${refColumn}, which does not exist`});continue}const name=postgresIdentifier(ref.constraintNames?.[0]??`${table}_${column.name}_fkey`);if(live.names.has(name)){reports.push({kind:"name-conflict",table,name,detail:`cannot add the foreign key for ${table}.${column.name} as ${name}: that name is taken`});continue}const actionsSql=[ref.onDelete?` ON DELETE ${foreignKeyAction(ref.onDelete)}`:"",ref.onUpdate?` ON UPDATE ${foreignKeyAction(ref.onUpdate)}`:""].join("");foreignKeys.push({kind:"add-foreign-key",table,name,detail:`add foreign key ${table}.${column.name} -> ${refTable}.${refColumn}`,sql:`ALTER TABLE ${quote(table)} ADD CONSTRAINT ${quote(name)} FOREIGN KEY (${quote(column.name)}) REFERENCES ${quote(refTable)} (${quote(refColumn)})${actionsSql}`,check:{kind:"orphans",sql:`SELECT COUNT(*) AS n FROM ${quote(table)} AS child WHERE child.${quote(column.name)} IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${quote(refTable)} AS parent WHERE parent.${quote(refColumn)} = child.${quote(column.name)})`,dependsOn:[column.name]}})}}return{actions:[...actions,...renames,...creates,...foreignKeys],reports,refused:[]}}function rows(result){return Array.isArray(result)?result:[]}function text(row,key){const value=row?.[key];return value===null||value===void 0?"":String(value)}export async function readPostgresAlignSchema(run){const tables=new Map;for(const row of rows(await run("SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema() AND table_type = 'BASE TABLE'")))tables.set(text(row,"table_name").toLowerCase(),new Map);for(const row of rows(await run("SELECT table_name, column_name, data_type, udt_name, character_maximum_length, is_nullable, column_default FROM information_schema.columns WHERE table_schema = current_schema()"))){const columns=tables.get(text(row,"table_name").toLowerCase());if(!columns)continue;const length=Number(row.character_maximum_length);columns.set(text(row,"column_name").toLowerCase(),{name:text(row,"column_name").toLowerCase(),dataType:text(row,"data_type").toLowerCase()==="user-defined"?"USER-DEFINED":text(row,"data_type").toLowerCase(),udtName:text(row,"udt_name").toLowerCase(),...Number.isFinite(length)&&length>0?{maxLength:length}:{},nullable:text(row,"is_nullable").toUpperCase()==="YES",...row.column_default!==null&&row.column_default!==void 0?{default:String(row.column_default)}:{}})}const indexes=[];for(const row of rows(await run(`
|
|
2
2
|
SELECT t.relname AS table_name, i.relname AS index_name, ix.indisunique AS is_unique, ix.indisprimary AS is_primary,
|
|
3
3
|
pg_get_indexdef(ix.indexrelid) AS definition, con.conname AS constraint_name
|
|
4
4
|
FROM pg_index ix
|
|
@@ -20,5 +20,5 @@ import{comparableIndexKey,indexShape,postgresIndexStatement}from"./index-shape";
|
|
|
20
20
|
JOIN pg_enum e ON e.enumtypid = t.oid
|
|
21
21
|
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
22
22
|
WHERE n.nspname = current_schema()
|
|
23
|
-
ORDER BY t.typname, e.enumsortorder`))){const type=text(row,"typname").toLowerCase();if(!enums.has(type))enums.set(type,[]);enums.get(type).push(text(row,"enumlabel"))}const names=new Set;for(const row of rows(await run("SELECT c.relname AS name FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = current_schema()")))names.add(text(row,"name").toLowerCase());for(const row of rows(await run("SELECT con.conname AS name FROM pg_constraint con JOIN pg_namespace n ON n.oid = con.connamespace WHERE n.nspname = current_schema()")))names.add(text(row,"name").toLowerCase());return{tables,indexes,foreignKeys,enums,names}}async function populatedTables(run,tables){const populated=new Set;for(const table of tables)if(rows(await run(`SELECT 1 AS present FROM ${quote(table)} LIMIT 1`)).length>0)populated.add(table);return populated}export async function evaluateAlignChecks(plan,run){const pending=new Set(plan.actions.filter((action)=>action.kind==="add-column").map((action)=>`${action.table}.${action.name}`)),kept=[];for(const action of plan.actions){if(!action.check){kept.push(action);continue}const unmet=action.check.dependsOn?.filter((column)=>pending.has(`${action.table}.${column}`))??[];if(unmet.length>0){plan.reports.push({kind:"unchecked",table:action.table,name:action.name,detail:`${action.detail}: not checked for ${action.check.kind==="orphans"?"orphan rows":"duplicate values"} first, because the probe reads ${unmet.map((column)=>`${action.table}.${column}`).join(", ")}, which this plan adds`});kept.push(action);continue}const count=Number(rows(await run(action.check.sql))[0]?.n??0);if(count>0){plan.refused.push({...action,reason:action.check.kind==="orphans"?`${count} row(s) in ${action.table} reference a missing parent - fix or delete them, then re-run`:`${count} duplicate value(s) in ${action.table} would violate it - de-duplicate them, then re-run`});continue}kept.push(action)}return kept}export async function alignSchema(options={}){const{ensureDatabaseConfigLoaded,resolvedDatabaseConfig}=await import("./utils");await ensureDatabaseConfigLoaded();const dialect=options.dialect??resolvedDatabaseConfig().driver,empty={actions:[],reports:[],refused:[]};if(dialect!=="postgres")return{supported:!1,dialect,plan:empty,applied:[]};const{generateFullModelMigration}=await import("./migrations"),generated=await generateFullModelMigration({dialect});if(!generated.isOk)throw generated.error;const run=options.run??(async(sql)=>{const{db}=await import("./utils");return rows(await db.unsafe(sql).execute())}),desired=desiredSchemaFrom(generated.value.plan,generated.value.statements),live=await readPostgresAlignSchema(run),candidates=[...desired.tables.keys()].filter((table)=>live.tables.has(table));live.populated=await populatedTables(run,candidates);let typeSatisfies;try{const qb=await import("bun-query-builder");if(qb.familyOfDeclared&&qb.familyOfSqlType&&qb.satisfies){const{familyOfDeclared,familyOfSqlType,satisfies}=qb;typeSatisfies=(declared,sqlType)=>satisfies(familyOfDeclared(declared),familyOfSqlType(sqlType),"postgres")}}catch{}const plan=planSchemaAlignment(desired,live,{widenIntegers:options.widenIntegers,typeSatisfies}),kept=await evaluateAlignChecks(plan,run);plan.actions=kept;if(!options.apply||kept.length===0)return{supported:!0,dialect,plan,applied:[]};const statements=kept.map((action)=>action.sql);await run(`${statements.join(`;
|
|
23
|
+
ORDER BY t.typname, e.enumsortorder`))){const type=text(row,"typname").toLowerCase();if(!enums.has(type))enums.set(type,[]);enums.get(type).push(text(row,"enumlabel"))}const names=new Set;for(const row of rows(await run("SELECT c.relname AS name FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = current_schema()")))names.add(text(row,"name").toLowerCase());for(const row of rows(await run("SELECT con.conname AS name FROM pg_constraint con JOIN pg_namespace n ON n.oid = con.connamespace WHERE n.nspname = current_schema()")))names.add(text(row,"name").toLowerCase());return{tables,indexes,foreignKeys,enums,names}}async function populatedTables(run,tables){const populated=new Set;for(const table of tables)if(rows(await run(`SELECT 1 AS present FROM ${quote(table)} LIMIT 1`)).length>0)populated.add(table);return populated}export async function evaluateAlignChecks(plan,run){const pending=new Set(plan.actions.filter((action)=>action.kind==="add-column").map((action)=>`${action.table}.${action.name}`)),kept=[];for(const action of plan.actions){if(!action.check){kept.push(action);continue}const unmet=action.check.dependsOn?.filter((column)=>pending.has(`${action.table}.${column}`))??[];if(unmet.length>0){plan.reports.push({kind:"unchecked",table:action.table,name:action.name,detail:`${action.detail}: not checked for ${action.check.kind==="orphans"?"orphan rows":"duplicate values"} first, because the probe reads ${unmet.map((column)=>`${action.table}.${column}`).join(", ")}, which this plan adds`});kept.push(action);continue}const count=Number(rows(await run(action.check.sql))[0]?.n??0);if(count>0){plan.refused.push({...action,reason:action.check.kind==="orphans"?`${count} row(s) in ${action.table} reference a missing parent - fix or delete them, then re-run`:`${count} duplicate value(s) in ${action.table} would violate it - de-duplicate them, then re-run`});continue}kept.push(action)}return kept}export async function alignSchema(options={}){const{ensureDatabaseConfigLoaded,resolvedDatabaseConfig}=await import("./utils");await ensureDatabaseConfigLoaded();const dialect=options.dialect??resolvedDatabaseConfig().driver,empty={actions:[],reports:[],refused:[]};if(dialect!=="postgres")return{supported:!1,dialect,plan:empty,applied:[]};const{generateFullModelMigration}=await import("./migrations"),generated=await generateFullModelMigration({dialect});if(!generated.isOk)throw generated.error;const run=options.run??(async(sql)=>{const{db}=await import("./utils");return rows(await db.unsafe(sql).execute())}),desired=desiredSchemaFrom(generated.value.plan,generated.value.statements),live=await readPostgresAlignSchema(run),candidates=[...desired.tables.keys()].filter((table)=>live.tables.has(table));live.populated=await populatedTables(run,candidates);let typeSatisfies;try{const qb=await import("bun-query-builder");if(qb.familyOfDeclared&&qb.familyOfSqlType&&qb.satisfies){const{familyOfDeclared,familyOfSqlType,satisfies}=qb;typeSatisfies=(declared,sqlType)=>satisfies(familyOfDeclared(declared),familyOfSqlType(sqlType),"postgres")}}catch{}const plan=planSchemaAlignment(desired,live,{widenIntegers:options.widenIntegers,relaxConstraints:options.relaxConstraints,typeSatisfies}),kept=await evaluateAlignChecks(plan,run);plan.actions=kept;if(!options.apply||kept.length===0)return{supported:!0,dialect,plan,applied:[]};const statements=kept.map((action)=>action.sql);await run(`${statements.join(`;
|
|
24
24
|
`)};`);return{supported:!0,dialect,plan,applied:statements}}
|
package/dist/trait-tables.js
CHANGED
|
@@ -80,4 +80,4 @@ import{log}from"@stacksjs/logging";import{db}from"./utils";import{resolvedDataba
|
|
|
80
80
|
created_at ${sql.datetime} NOT NULL,
|
|
81
81
|
read_at ${sql.nullableTimestamp},
|
|
82
82
|
UNIQUE (conversation_id, sender_id, client_key)
|
|
83
|
-
)`]}export function traitTablePrerequisiteSql(corpus,sql){const statements=sqlStatementsOf(corpus).map((statement)=>statement.replace(/'(?:''|[^'])*'/g,"''")),create=new RegExp(`\\bCREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?${SQL_IDENTIFIER}\\s*\\(`,"i"),references=(statement)=>[...statement.matchAll(new RegExp(`\\bREFERENCES\\s+${SQL_IDENTIFIER}\\s*\\(`,"gi"))].map((match)=>match[1].toLowerCase()),declared=new Set(statements.map((statement)=>create.exec(statement)?.[1]?.toLowerCase()).filter(Boolean)),native=new Map([...messagingTableSql(sql),commentablesTableSql(sql),taggablesTableSql(sql),categorizablesTableSql(sql),commentableUpvotesTableSql(sql),taggableModelsTableSql(sql),categorizableModelsTableSql(sql)].map((statement)=>[create.exec(statement)[1].toLowerCase(),statement])),plan=[],visited=new Set;function visit(table){if(declared.has(table)||visited.has(table)||!native.has(table))return;visited.add(table);const statement=native.get(table);for(const parent of references(statement))visit(parent);plan.push(statement)}for(const statement of statements)for(const table of references(statement))visit(table);return plan}export async function migrateTraitTables(options={}){const dbDriver=resolvedDatabaseDriver(),sql=sqlHelpers(dbDriver);if(options.verbose)log.info(`Creating polymorphic trait tables for ${dbDriver}...`);try{for(const statement of messagingTableSql(sql))await db.unsafe(statement).execute();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{table,column,definition}of traitTableColumnGuarantees(sql))try{await db.unsafe(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).execute();if(options.verbose)log.info(`Added missing ${table}.${column}`)}catch(error){if(!isDuplicateColumnError(error))throw 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}}}
|
|
83
|
+
)`]}export function traitTablePrerequisiteSql(corpus,sql){const statements=sqlStatementsOf(corpus).map((statement)=>statement.replace(/'(?:''|[^'])*'/g,"''")),create=new RegExp(`\\bCREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?${SQL_IDENTIFIER}\\s*\\(`,"i"),references=(statement)=>[...statement.matchAll(new RegExp(`\\bREFERENCES\\s+${SQL_IDENTIFIER}\\s*\\(`,"gi"))].map((match)=>match[1].toLowerCase()),declared=new Set(statements.map((statement)=>create.exec(statement)?.[1]?.toLowerCase()).filter(Boolean)),native=new Map([...messagingTableSql(sql),commentablesTableSql(sql),taggablesTableSql(sql),categorizablesTableSql(sql),commentableUpvotesTableSql(sql),taggableModelsTableSql(sql),categorizableModelsTableSql(sql)].map((statement)=>[create.exec(statement)[1].toLowerCase(),statement])),plan=[],visited=new Set;function visit(table){if(declared.has(table)||visited.has(table)||!native.has(table))return;visited.add(table);const statement=native.get(table);for(const parent of references(statement))visit(parent);plan.push(statement)}for(const statement of statements)for(const table of references(statement))visit(table);return plan}async function reconcileEmptySqlitePivots(sql){if(!sql.isSqlite)return;for(const[table,column]of[["taggable_models","taggable_id"],["categorizable_models","categorizable_id"]]){if((await db.unsafe(`PRAGMA table_info(${table})`).execute()).some((existing)=>existing.name===column))continue;if((await db.unsafe(`SELECT 1 FROM ${table} LIMIT 1`).execute()).length>0)throw Error(`Cannot infer ${table}.${column} for existing rows. Restore the owner IDs from an authoritative backup or repair the pivot explicitly, then rerun migrate.`);try{await db.unsafe(`ALTER TABLE ${table} ADD COLUMN ${column} ${sql.bigInteger} NOT NULL`).execute()}catch(error){if(!isDuplicateColumnError(error))throw error}}}export async function migrateTraitTables(options={}){const dbDriver=resolvedDatabaseDriver(),sql=sqlHelpers(dbDriver);if(options.verbose)log.info(`Creating polymorphic trait tables for ${dbDriver}...`);try{for(const statement of messagingTableSql(sql))await db.unsafe(statement).execute();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{table,column,definition}of traitTableColumnGuarantees(sql))try{await db.unsafe(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).execute();if(options.verbose)log.info(`Added missing ${table}.${column}`)}catch(error){if(!isDuplicateColumnError(error))throw error}await reconcileEmptySqlitePivots(sql);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/dist/utils.d.ts
CHANGED
|
@@ -35,6 +35,13 @@ declare function getDialect(): QueryBuilderDialect;
|
|
|
35
35
|
* loaded after this module (stacksjs/stacks#2351).
|
|
36
36
|
*/
|
|
37
37
|
export declare function qbSnapshotDir(): string;
|
|
38
|
+
/**
|
|
39
|
+
* The user's directory, captured before either connection setup or migration
|
|
40
|
+
* generation writes a resolved corpus into shared query-builder config.
|
|
41
|
+
* Capturing only in the generator was too late after MySQL connection setup:
|
|
42
|
+
* a later Postgres generation inherited the MySQL directory (#2949).
|
|
43
|
+
*/
|
|
44
|
+
export declare function qbMigrationDir(): string | undefined;
|
|
38
45
|
export declare function createDatabaseQueryHooks(dispatch: (event: DatabaseQueryLogEvent) => void | Promise<void>): QueryHooks;
|
|
39
46
|
export declare function ensureDatabaseConfigLoaded(): Promise<void>;
|
|
40
47
|
/**
|
package/dist/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AsyncLocalStorage}from"node:async_hooks";import{isPromise,isProxy}from"node:util/types";import{config as queryBuilderConfig,createQueryBuilder,registerPersistentQueryHooks,resetConnection as resetQueryBuilderConnection,setConfig}from"@stacksjs/query-builder";import{SQL}from"bun";import{closeDatabaseConnectionsAndPending,retireDatabaseConnections}from"./connection-lifecycle";import{runInTransactionScope}from"./transaction-context";import{env as envVars}from"@stacksjs/env/runtime";import{getConnectionDefaults}from"./defaults";import{isLibsqlDriver,isMysqlWire,isVitessSharded,normalizeDatabaseDriver,setConfiguredDatabaseDriver,toQueryBuilderDialect}from"./dialect";import{relativeMigrationDirectory,resolveMigrationDirectory,snapshotDirForQueryBuilder}from"./migration-path";import{configureDatabaseRoutingContext,contextInTransaction,markContextWrote,replicaConnectionKey,resolveReplicaConnection,runInDatabaseRoutingContext,selectReplica,shouldRouteToReplica,withDatabaseRoutingContext,withTransactionContext}from"./replicas";import{aggregateFunctions}from"./types";const sqliteDefaults=getConnectionDefaults("sqlite",envVars),mysqlDefaults=getConnectionDefaults("mysql",envVars),postgresDefaults=getConnectionDefaults("postgres",envVars);let appEnv=envVars.APP_ENV||"local",dbDriver=normalizeDatabaseDriver(envVars.DB_CONNECTION||"sqlite"),queryBuilderDialect=toQueryBuilderDialect(dbDriver),queryLoggingEnabled=envVars.DB_QUERY_LOGGING_ENABLED??!isProductionEnvironment(appEnv);const envDbConfig=()=>({connections:{sqlite:{database:sqliteDefaults.database,prefix:""},mysql:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},singlestore:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},vitess:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:15306,prefix:"",sharded:isVitessSharded()},postgres:{name:postgresDefaults.database,host:postgresDefaults.host,username:postgresDefaults.username,password:postgresDefaults.password,port:postgresDefaults.port,prefix:""},turso:{url:envVars.TURSO_DATABASE_URL||"",authToken:envVars.TURSO_AUTH_TOKEN||"",prefix:""}}});let dbConfig=envDbConfig(),dbConfigLockTail=Promise.resolve();const DB_CONFIG_LOCK_MAX_HOLD_MS=60000;export function acquireDbConfigLock(){let release=()=>{};const held=new Promise((resolve)=>{release=resolve}),acquired=dbConfigLockTail.then(()=>{const watchdog=setTimeout(()=>{console.warn(`[database] the db config lock was held for over ${DB_CONFIG_LOCK_MAX_HOLD_MS/1000}s. Releasing it so the queue can proceed - a test file most likely failed before its afterAll ran.`);release()},DB_CONFIG_LOCK_MAX_HOLD_MS);watchdog.unref?.();held.then(()=>clearTimeout(watchdog));return release});dbConfigLockTail=dbConfigLockTail.then(()=>held).catch(()=>{});return acquired}export function initializeDbConfig(config){if(config?.app?.env)appEnv=config.app.env;if(config?.database?.default){dbDriver=normalizeDatabaseDriver(config.database.default);setConfiguredDatabaseDriver(dbDriver)}queryBuilderDialect=toQueryBuilderDialect(dbDriver);if(config?.database?.connections){const baseline=envDbConfig(),supplied=config.database.connections,connections={...baseline.connections};for(const[name,connection]of Object.entries(supplied))if(connection&&typeof connection==="object")connections[name]={...connections[name]??{},...connection};dbConfig={...config.database,connections}}configureDatabaseRoutingContext(getReplicas().length>0);queryLoggingEnabled=config?.database?.queryLogging?.enabled??envVars.DB_QUERY_LOGGING_ENABLED??!isProductionEnvironment(appEnv);syncDatabaseQueryHooks();updateQueryBuilderConfig();_dbInstance=null;_replicaInstances=new Map}function getEnv(){return appEnv}function isProductionEnvironment(value){return value==="production"||value==="prod"}function getDriver(){return dbDriver}function getDatabaseConfig(){return dbConfig}export function resolvedDatabaseConfig(){return{driver:dbDriver,connections:dbConfig.connections}}function getDialect(){return queryBuilderDialect}export{getDialect as getDatabaseDialect};function getDbConfig(){return{url:void 0,authToken:void 0,...getDriverDbConfig()}}function getDriverDbConfig(){const driver=getDriver(),database=getDatabaseConfig(),env=getEnv();if(isLibsqlDriver(driver)){const turso=database.connections?.turso,url=turso?.url||envVars.TURSO_DATABASE_URL||"";if(!url)throw Error("[database] DB_CONNECTION=turso needs the database URL: set TURSO_DATABASE_URL (libsql://<db>-<org>.turso.io, or http://127.0.0.1:8080 for `turso dev`) or connections.turso.url in config/database.ts.");return{database:url,url,authToken:turso?.authToken||envVars.TURSO_AUTH_TOKEN||void 0}}if(driver==="sqlite"){const defaultName=env!=="testing"?"database/stacks.sqlite":"database/stacks_testing.sqlite";return{database:database.connections?.sqlite?.database??defaultName}}if(driver==="mysql")return{database:database.connections?.mysql?.name||"stacks",host:database.connections?.mysql?.host??"127.0.0.1",username:database.connections?.mysql?.username??"root",password:database.connections?.mysql?.password??"",port:database.connections?.mysql?.port??3306};if(driver==="singlestore")return{database:database.connections?.singlestore?.name||"stacks",host:database.connections?.singlestore?.host??"127.0.0.1",username:database.connections?.singlestore?.username??"root",password:database.connections?.singlestore?.password??"",port:database.connections?.singlestore?.port??3306};if(driver==="vitess")return{database:database.connections?.vitess?.name||"stacks",host:database.connections?.vitess?.host??"127.0.0.1",username:database.connections?.vitess?.username??"root",password:database.connections?.vitess?.password??"",port:database.connections?.vitess?.port??15306};if(driver==="postgres"){const dbName=database.connections?.postgres?.name??"stacks";return{database:env==="testing"?`${dbName}_testing`:dbName,host:database.connections?.postgres?.host??"127.0.0.1",username:database.connections?.postgres?.username??"",password:database.connections?.postgres?.password??"",port:database.connections?.postgres?.port??5432}}return{database:":memory:"}}export const QB_SNAPSHOT_DIR="storage/framework/database";export function qbSnapshotDir(){return snapshotDirForQueryBuilder()}export const RAW_QUERY_SOFT_DELETE_CONFIG={enabled:!1,column:"deleted_at",defaultFilter:!0};export function createDatabaseQueryHooks(dispatch){function forward(event){try{Promise.resolve(dispatch(event)).catch(()=>{})}catch{}}return{onQueryEnd:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs}),onQueryError:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs,error:event.error})}}let queryLoggerModule;function forwardDatabaseQuery(event){transactionConnection.exit(()=>{queryLoggerModule??=import("./query-logger").catch((error)=>{queryLoggerModule=void 0;throw error});queryLoggerModule.then(({logQuery})=>logQuery(event)).catch(()=>{})})}let unregisterDatabaseQueryHooks;function syncDatabaseQueryHooks(){const shouldInstall=!isProductionEnvironment(appEnv)||queryLoggingEnabled;if(shouldInstall&&!unregisterDatabaseQueryHooks)unregisterDatabaseQueryHooks=registerPersistentQueryHooks(createDatabaseQueryHooks(forwardDatabaseQuery));else if(!shouldInstall&&unregisterDatabaseQueryHooks){unregisterDatabaseQueryHooks();unregisterDatabaseQueryHooks=void 0}}syncDatabaseQueryHooks();function getPoolConfig(){const driver=getDriver();if(driver==="sqlite"||isLibsqlDriver(driver))return;return getDatabaseConfig().connections?.[driver]?.pool}const EMPTY_REPLICAS=[];function getReplicas(){const driver=getDriver();if(driver==="sqlite"||isLibsqlDriver(driver))return EMPTY_REPLICAS;return getDatabaseConfig().connections?.[driver]?.replicas??EMPTY_REPLICAS}function getReadPolicy(){return getDatabaseConfig().reads??{}}export{runInDatabaseRoutingContext,withDatabaseRoutingContext};function toBunPoolOptions(pool){if(!pool)return{};const options={};if(pool.max!==void 0)options.max=pool.max;if(pool.idleTimeoutMs!==void 0)options.idleTimeout=Math.round(pool.idleTimeoutMs/1000);if(pool.acquireTimeoutMs!==void 0)options.connectionTimeout=Math.round(pool.acquireTimeoutMs/1000);if(pool.maxLifetimeMs!==void 0)options.maxLifetime=Math.round(pool.maxLifetimeMs/1000);if(pool.preparedStatements!==void 0)options.prepare=pool.preparedStatements;return options}function updateQueryBuilderConfig(){const dialect=getDialect(),dbConfigForQb=getDbConfig(),pool=getPoolConfig();setConfig({dialect,vitess:{sharded:isVitessSharded(dbConfig.connections.vitess?.sharded)},database:pool?{...dbConfigForQb,pool}:dbConfigForQb,verbose:getEnv()!=="production",snapshotDir:qbSnapshotDir(),migrationDir:relativeMigrationDirectory(resolveMigrationDirectory(toQueryBuilderDialect(dialect),{snapshotDir:qbSnapshotDir()})),timestamps:{createdAt:"created_at",updatedAt:"updated_at",defaultOrderColumn:"created_at"},softDeletes:RAW_QUERY_SOFT_DELETE_CONFIG})}updateQueryBuilderConfig();let _dbInstance=null,_configInitPromise=null;function ensureConfigLoaded(){if(!_configInitPromise)_configInitPromise=(async()=>{try{const{config,overridesReady}=await import("@stacksjs/config/runtime");await overridesReady;if(config){initializeDbConfig(config);_dbInstance=null}}catch{}})();return _configInitPromise}export async function ensureDatabaseConfigLoaded(){await ensureConfigLoaded()}export{applySqlitePragmas,SQLITE_BOOTSTRAP_PRAGMAS}from"@stacksjs/query-builder";const sqliteTxOwner=new AsyncLocalStorage;let sqliteTxTail=Promise.resolve();function runOwnedSqliteTransaction(run,parent){const owner={parent,accepting:!0,transactions:0,completionTail:Promise.resolve()};return sqliteTxOwner.run(owner,async()=>{try{return await run()}finally{owner.accepting=!1;await owner.completionTail}})}function serializeSqliteTransaction(run){let owner=sqliteTxOwner.getStore();while(owner&&!owner.accepting)owner=owner.parent;if(owner?.accepting&&owner.transactions>0)return sqliteTxOwner.run(owner,run);const completionOwner=owner?.accepting?owner:void 0,result=(completionOwner?.completionTail??sqliteTxTail).then(()=>runOwnedSqliteTransaction(run,completionOwner)),tail=result.then(()=>{return},()=>{return});if(completionOwner)completionOwner.completionTail=tail;else sqliteTxTail=tail;return result}function applySqliteTransactionSerialization(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>serializeSqliteTransaction(()=>original(...args))}const dispatchScopedBuilders=new WeakSet,transactionConnection=new AsyncLocalStorage;function activeTransactionConnection(){const scope=transactionConnection.getStore();if(!scope)return;assertTransactionConnection(scope);return scope.connection}function assertTransactionConnection(scope){for(let owner=scope;owner;owner=owner.parent)if(!owner.active)throw Error("Database work cannot continue from a closed transaction callback.");if(scope.nested)throw Error("Await the active nested transaction before issuing more database work.")}function guardTransactionQuery(value){const scope=transactionConnection.getStore();if(!scope||!value||typeof value!=="object"||typeof value.execute!=="function")return value;const wrappers=new WeakMap,wrap=(target)=>{const cached=wrappers.get(target);if(cached)return cached;const proxy=new Proxy(target,{get(object,property){const member=Reflect.get(object,property,object);if(typeof member!=="function")return member;return(...args)=>{assertTransactionConnection(scope);if(transactionConnection.getStore()!==scope)throw Error("A transaction query must execute in the callback that created it.");const guardedArgs=args.map((arg)=>typeof arg==="function"?function(...values){return Reflect.apply(arg,this,values.map((value)=>value&&typeof value==="object"&&typeof Reflect.get(value,"execute")==="function"?wrap(value):value))}:arg),result=Reflect.apply(member,object,guardedArgs);if(result===object)return proxy;if(result&&typeof result==="object"&&!isPromise(result)&&(typeof Reflect.get(result,"execute")==="function"||typeof Reflect.get(result,"next")==="function"))return wrap(result);return result}}});wrappers.set(target,proxy);return proxy};return wrap(value)}function applyTransactionDispatchScope(instance){if(dispatchScopedBuilders.has(instance))return;dispatchScopedBuilders.add(instance);let defaultOptions={};const setDefaults=instance.setTransactionDefaults.bind(instance);instance.setTransactionDefaults=(defaults)=>{setDefaults(defaults);defaultOptions={...defaultOptions,...defaults}};for(const method of["transaction","savepoint"]){const original=instance[method].bind(instance);instance[method]=async(callback,options)=>{const active=activeTransactionConnection();if(active&&active!==instance)return active[method](callback,options);const parent=transactionConnection.getStore(),effectiveOptions={...defaultOptions,...options},afterCommit=method==="transaction"?effectiveOptions.afterCommit:void 0,parentObserver=(listener)=>listener?(...args)=>{const nested=parent?.nested;if(parent)parent.nested=!1;try{listener(...args)}finally{if(parent)parent.nested=nested}}:void 0,result=await runInTransactionScope(async(runAttempt)=>{const owner=sqliteTxOwner.getStore();if(owner)owner.transactions++;if(parent)parent.nested=!0;try{const runCallback=AsyncLocalStorage.bind((tx)=>runAttempt(async()=>{applyTransactionDispatchScope(tx);const scope={connection:tx,parent,active:!0,nested:!1};return transactionConnection.run(scope,async()=>{try{return await callback(tx)}finally{scope.active=!1}})}));return await original(runCallback,method==="transaction"?{...options,afterCommit:void 0,onRollback:parentObserver(effectiveOptions.onRollback),afterRollback:parentObserver(effectiveOptions.afterRollback),onRetry:parentObserver(effectiveOptions.onRetry)}:options)}finally{if(parent)parent.nested=!1;if(owner)owner.transactions--}});await afterCommit?.();return result}}}function applyTransactionRoutingContext(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>withTransactionContext(()=>original(...args))}function getDb(){if(_databaseClosePromise)throw Error("Database connections are shutting down");const active=activeTransactionConnection();if(active)return active;if(!_dbInstance){updateQueryBuilderConfig();_dbInstance=createQueryBuilder();applyTransactionDispatchScope(_dbInstance);if(getDialect()==="sqlite")applySqliteTransactionSerialization(_dbInstance);applyTransactionRoutingContext(_dbInstance)}return _dbInstance}let _replicaInstances=new Map,_databaseClosePromise=null;const _retiredConnectionClosures=new Set;export function resetDatabaseConnection(){const connections=[..._dbInstance?[_dbInstance]:[],..._replicaInstances.values()];if(connections.length>0)retireDatabaseConnections(_retiredConnectionClosures,connections,(error)=>console.error(`[database] Failed to close retired connection: ${error.message}`));resetQueryBuilderConnection();lastGeneralSqliteStatement=void 0;_dbInstance=null;_replicaInstances=new Map}export function closeDatabaseConnection(){if(_databaseClosePromise)return _databaseClosePromise;const connections=[..._dbInstance?[_dbInstance]:[],..._replicaInstances.values()];_dbInstance=null;_replicaInstances=new Map;lastGeneralSqliteStatement=void 0;const closing=(async()=>{try{await closeDatabaseConnectionsAndPending(connections,_retiredConnectionClosures)}finally{resetQueryBuilderConnection()}})();_databaseClosePromise=closing;closing.then(()=>{if(_databaseClosePromise===closing)_databaseClosePromise=null},()=>{if(_databaseClosePromise===closing)_databaseClosePromise=null});return closing}function getReplicaDb(replica){const primary=getDbConfig(),resolved=resolveReplicaConnection(replica,primary),key=replicaConnectionKey(resolved),cached=_replicaInstances.get(key);if(cached)return cached;const scheme=isMysqlWire(getDriver())?"mysql":"postgres",auth=resolved.username?`${encodeURIComponent(resolved.username)}:${encodeURIComponent(resolved.password??"")}@`:"",url=`${scheme}://${auth}${resolved.host}:${resolved.port}/${resolved.database}`,sql=new SQL(url,{...toBunPoolOptions(getPoolConfig()),database:resolved.database}),instance=createQueryBuilder({sql});_replicaInstances.set(key,instance);return instance}function getReadDb(){const active=activeTransactionConnection();if(active)return active;const replicas=getReplicas();if(replicas.length===0)return getDb();const policy=getReadPolicy();if(!shouldRouteToReplica({policy,replicas}))return getDb();const replica=selectReplica(replicas,policy.strategy);return replica?getReplicaDb(replica):getDb()}function getExplicitReadDb(){const active=activeTransactionConnection();if(active)return active;const replicas=getReplicas();if(!replicas.length||contextInTransaction())return getDb();const replica=selectReplica(replicas,getReadPolicy().strategy);return replica?getReplicaDb(replica):getDb()}const WRITE_ENTRY_POINTS=new Set(["insertInto","updateTable","deleteFrom","create","createMany","insertOrIgnore","insertGetId","updateOrInsert","upsert"]),READ_ENTRY_POINTS=new Set(["selectFrom","selectFromSub","select"]);ensureConfigLoaded();export function asRows(rows){return rows}export function asRow(row){return row}const SIMPLE_SQLITE_TABLE=/^[A-Z_][A-Z0-9_]*$/i,SIMPLE_SQLITE_COLUMN=/^[A-Z_][A-Z0-9_]*(?:\.[A-Z_][A-Z0-9_]*)?$/i,SIMPLE_SQLITE_SELECTION=/^[A-Z_][A-Z0-9_]*(?:\.[A-Z_][A-Z0-9_]*)?(?:\s+AS\s+[A-Z_][A-Z0-9_]*)?$/i,SIMPLE_SQLITE_OPERATORS=new Set(["=","!=","<>","<","<=",">",">=","like","not like"]),SQLITE_IDENTIFIER_CACHE_LIMIT=512;function memoizeSqliteIdentifier(pattern){const valid=new Set;return(value)=>{if(valid.has(value))return!0;if(!pattern.test(value))return!1;if(valid.size<SQLITE_IDENTIFIER_CACHE_LIMIT)valid.add(value);return!0}}const isSimpleSqliteTable=memoizeSqliteIdentifier(SIMPLE_SQLITE_TABLE),isSimpleSqliteColumn=memoizeSqliteIdentifier(SIMPLE_SQLITE_COLUMN),isSimpleSqliteSelection=memoizeSqliteIdentifier(SIMPLE_SQLITE_SELECTION);let lastParameterizedSqliteSelect,lastUnparameterizedSqliteSelect,lastSqliteSelection;const GUARDED_SQLITE_SELECT_METHODS=new Set(["select","where","limit","execute","executeSync","executeTakeFirstSync"]);function hasActiveQueryBuilderHooks(){return Boolean(queryBuilderConfig.hooks&&Object.values(queryBuilderConfig.hooks).some((value)=>value!==void 0))}function resolveDeferredSqliteTerminal(target,property,executeStatement,executeFirstStatement,materialize,proxy){if(property==="get"){target.get=target.execute;return target.execute}if(property==="selectAll"){const selectAll=()=>proxy;target.selectAll=selectAll;return selectAll}if(property==="first"){const first=async()=>executeFirstStatement();target.first=first;return first}if(property==="firstOrFail"||property==="executeTakeFirstOrThrow"){const firstOrFail=async()=>{const row=executeFirstStatement();if(row===void 0)throw Error("Record not found");return row};target.firstOrFail=firstOrFail;target.executeTakeFirstOrThrow=firstOrFail;return firstOrFail}if(property==="exists"||property==="doesntExist"){const findsRow=property==="exists",check=async()=>executeFirstStatement()!==void 0===findsRow;target[property]=check;return check}if(property==="value"){const value=async(column)=>executeFirstStatement()?.[column];target.value=value;return value}if(property==="count"||property==="sum"||property==="avg"||property==="min"||property==="max"){const emptyValue=property==="min"||property==="max"?null:0,aggregate=(...args)=>runDeferredSqliteAggregate(property,args,emptyValue,materialize,executeStatement);target[property]=aggregate;return aggregate}if(property==="pluck"){const pluck=(...args)=>{if(args.length!==1){const builder=materialize();return builder.pluck.call(builder,...args)}try{const column=args[0],rows=executeStatement(),values=Array(rows.length);for(let index=0;index<rows.length;index++)values[index]=rows[index]?.[column];return Promise.resolve(values)}catch(error){return Promise.reject(error)}};target.pluck=pluck;return pluck}}function runDeferredSqliteAggregate(name,args,emptyValue,materialize,executeStatement){const column=args[0],acceptsNoColumn=name==="count"&&args.length===0;if(!acceptsNoColumn&&(args.length!==1||typeof column!=="string"||!isSimpleSqliteColumn(column))){const builder=materialize();return builder[name].call(builder,...args)}try{const expression=`${name.toUpperCase()}(${acceptsNoColumn?"*":column}) AS aggregate`,value=executeStatement(!1,expression)[0]?.aggregate??emptyValue;return Promise.resolve(name==="count"||name==="sum"||name==="avg"?Number(value):value)}catch(error){return Promise.reject(error)}}const fastSqliteDatabaseCache=new WeakMap;let lastGeneralSqliteStatement;function fastSqliteDatabase(instance){if(fastSqliteDatabaseCache.has(instance))return fastSqliteDatabaseCache.get(instance)??void 0;const database=instance.sql?._wrapper?.database,resolved=database&&typeof database.query==="function"?database:null;fastSqliteDatabaseCache.set(instance,resolved);return resolved??void 0}function runFastSqliteSql(instance,sqliteDatabase,sql,params){if(sqliteDatabase){let cached=lastGeneralSqliteStatement;if(!cached||cached.database!==sqliteDatabase||cached.sql!==sql){cached={database:sqliteDatabase,sql,statement:sqliteDatabase.query(sql)};lastGeneralSqliteStatement=cached}return params?cached.statement.all(...params):cached.statement.all()}return instance.unsafe(sql,params).executeSync()}let lastSqlitePlaceholders;function renderSqlitePlaceholders(length){const cached=lastSqlitePlaceholders;if(cached&&cached.length===length)return cached.sql;const sql=Array(length).fill("?").join(", ");if(typeof length==="number")lastSqlitePlaceholders={length,sql};return sql}function snapshotSimpleSqliteMembershipValues(values){if(isProxy(values)||Object.getPrototypeOf(values)!==Array.prototype||Object.hasOwn(values,Symbol.iterator))return;const snapshot=Array(values.length);for(let index=0;index<values.length;index++){const descriptor=Object.getOwnPropertyDescriptor(values,index);if(!descriptor||!("value"in descriptor))return;snapshot[index]=descriptor.value}return snapshot}function createDeferredSqliteSelect(instance,table){const sqliteDatabase=fastSqliteDatabase(instance);let selectKeyword="SELECT",columns,selectedColumnsSql,predicateColumn,predicateOperator,predicateValue,predicateParameterized=!0,predicateValues,additionalPredicates,orderings,rowLimit,rowOffset,materialized;const materialize=()=>{if(materialized)return materialized;let builder=instance.selectFrom(table);if(columns)builder=builder.select.call(builder,columns);if(selectKeyword==="SELECT DISTINCT")builder=builder.distinct.call(builder);if(predicateColumn!==void 0){if(predicateValues)if(predicateOperator==="LIKE LOWER"||predicateOperator==="NOT LIKE LOWER")builder=builder[predicateOperator==="LIKE LOWER"?"whereILike":"whereNotILike"].call(builder,predicateColumn.slice(6,-1),predicateValues[0]);else builder=builder[predicateOperator==="IN"?"whereIn":"whereNotIn"].call(builder,predicateColumn,predicateValues);else if(predicateParameterized)builder=builder.where.call(builder,predicateColumn,predicateOperator,predicateValue);else builder=builder[predicateOperator==="IS NULL"?"whereNull":"whereNotNull"].call(builder,predicateColumn);if(additionalPredicates)for(const predicate of additionalPredicates)if(predicate.values)if(predicate.operator==="LIKE LOWER"||predicate.operator==="NOT LIKE LOWER"){const method=predicate.operator==="LIKE LOWER"?"whereILike":"whereNotILike";builder=builder[method].call(builder,predicate.column.slice(6,-1),predicate.values[0])}else{const method=predicate.operator==="IN"?"whereIn":"whereNotIn";builder=builder[method].call(builder,predicate.column,predicate.values)}else if(predicate.parameterized)builder=builder.where.call(builder,predicate.column,predicate.operator,predicate.value);else{const method=predicate.operator==="IS NULL"?"whereNull":"whereNotNull";builder=builder[method].call(builder,predicate.column)}}if(orderings){const apply=builder.orderBy;for(const ordering of orderings)builder=apply.call(builder,ordering.column,ordering.direction)}if(rowLimit!==void 0)builder=builder.limit.call(builder,rowLimit);if(rowOffset!==void 0)builder=builder.offset.call(builder,rowOffset);materialized=builder;return builder};let proxy;const executeStatement=(firstOnly=!1,selection)=>{const selected=selection??selectedColumnsSql??"*",effectiveLimit=rowLimit??(firstOnly&&rowOffset===void 0?1:void 0);if(predicateColumn===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastUnparameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.limit===effectiveLimit){if(sqliteDatabase){const statement=cached.statement??=sqliteDatabase.query(cached.sql);if(effectiveLimit===1){const row=statement.get();return row===null?[]:[row]}return statement.all()}return runFastSqliteSql(instance,sqliteDatabase,cached.sql)}const limit=effectiveLimit===void 0?"":` LIMIT ${effectiveLimit}`,sql=`${selectKeyword} ${selected} FROM ${table}${limit}`;lastUnparameterizedSqliteSelect={instance,selectKeyword,selected,table,limit:effectiveLimit,sql,statement:sqliteDatabase?.query(sql)};if(lastUnparameterizedSqliteSelect.statement){if(effectiveLimit===1){const row=lastUnparameterizedSqliteSelect.statement.get();return row===null?[]:[row]}return lastUnparameterizedSqliteSelect.statement.all()}return runFastSqliteSql(instance,sqliteDatabase,sql)}if(predicateColumn!==void 0&&predicateValues===void 0&&predicateParameterized&&additionalPredicates===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastParameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.predicateColumn===predicateColumn&&cached.predicateOperator===predicateOperator&&cached.limit===effectiveLimit){if(sqliteDatabase){const statement=cached.statement??=sqliteDatabase.query(cached.sql);if(effectiveLimit===1){const row=statement.get(predicateValue);return row===null?[]:[row]}return statement.all(predicateValue)}return runFastSqliteSql(instance,sqliteDatabase,cached.sql,[predicateValue])}const sql=`${selectKeyword} ${selected} FROM ${table} WHERE ${predicateColumn} ${predicateOperator} ?${effectiveLimit===void 0?"":` LIMIT ${effectiveLimit}`}`;lastParameterizedSqliteSelect={instance,selectKeyword,selected,table,predicateColumn,predicateOperator,limit:effectiveLimit,sql,statement:sqliteDatabase?.query(sql)};if(lastParameterizedSqliteSelect.statement){if(effectiveLimit===1){const row=lastParameterizedSqliteSelect.statement.get(predicateValue);return row===null?[]:[row]}return lastParameterizedSqliteSelect.statement.all(predicateValue)}return runFastSqliteSql(instance,sqliteDatabase,sql,[predicateValue])}let query=`${selectKeyword} ${selected} FROM ${table}`;const params=[];if(predicateColumn!==void 0){query+=` WHERE ${predicateColumn} ${predicateOperator}`;if(predicateValues){query+=` (${renderSqlitePlaceholders(predicateValues.length)})`;params.push(...predicateValues)}else if(predicateParameterized){query+=" ?";params.push(predicateValue)}if(additionalPredicates){const additionalCount=additionalPredicates.length;query+=" AND ";for(let index=0;index<additionalCount;index++){if(index>0)query+=" AND ";const predicate=additionalPredicates[index];if(predicate.values){params.push(...predicate.values);query+=`${predicate.column} ${predicate.operator} (${renderSqlitePlaceholders(predicate.values.length)})`;continue}if(predicate.parameterized){params.push(predicate.value);query+=`${predicate.column} ${predicate.operator} ?`;continue}query+=`${predicate.column} ${predicate.operator}`}}}if(orderings)query+=` ORDER BY ${orderings.map((ordering)=>`${ordering.column} ${ordering.direction.toUpperCase()}`).join(", ")}`;if(effectiveLimit!==void 0)query+=` LIMIT ${effectiveLimit}`;if(rowOffset!==void 0)query+=` OFFSET ${rowOffset}`;return runFastSqliteSql(instance,sqliteDatabase,query,params)},executeFirstStatement=()=>{if(rowLimit===0)return;const selected=selectedColumnsSql??"*";if(predicateColumn===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastUnparameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.limit===1){if(sqliteDatabase)return(cached.statement??=sqliteDatabase.query(cached.sql)).get()??void 0;return runFastSqliteSql(instance,sqliteDatabase,cached.sql)[0]}const sql=`${selectKeyword} ${selected} FROM ${table} LIMIT 1`,statement=sqliteDatabase?.query(sql);lastUnparameterizedSqliteSelect={instance,selectKeyword,selected,table,limit:1,sql,statement};return statement?statement.get()??void 0:runFastSqliteSql(instance,sqliteDatabase,sql)[0]}if(predicateColumn!==void 0&&predicateValues===void 0&&predicateParameterized&&additionalPredicates===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastParameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.predicateColumn===predicateColumn&&cached.predicateOperator===predicateOperator&&cached.limit===1){if(sqliteDatabase)return(cached.statement??=sqliteDatabase.query(cached.sql)).get(predicateValue)??void 0;return runFastSqliteSql(instance,sqliteDatabase,cached.sql,[predicateValue])[0]}const sql=`${selectKeyword} ${selected} FROM ${table} WHERE ${predicateColumn} ${predicateOperator} ? LIMIT 1`,statement=sqliteDatabase?.query(sql);lastParameterizedSqliteSelect={instance,selectKeyword,selected,table,predicateColumn,predicateOperator,limit:1,sql,statement};return statement?statement.get(predicateValue)??void 0:runFastSqliteSql(instance,sqliteDatabase,sql,[predicateValue])[0]}return executeStatement(!0)[0]},createExtensions=()=>({distinct(){if(selectKeyword==="SELECT DISTINCT")return materialize().distinct();selectKeyword="SELECT DISTINCT";return proxy},where(column,operator,value){if(column!==null&&typeof column==="object"&&!Array.isArray(column)&&operator===void 0&&value===void 0){const prototype=Object.getPrototypeOf(column),entries=prototype===Object.prototype||prototype===null?Object.entries(column):[];if(entries.length>0&&entries.every(([key])=>isSimpleSqliteColumn(key))){for(const[key,entryValue]of entries)if(predicateColumn===void 0){predicateColumn=key;predicateOperator="=";predicateValue=entryValue;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column:key,operator:"=",value:entryValue,parameterized:!0});return proxy}}const builder=materialize();return builder.where.call(builder,column,operator,value)},whereNull(column){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNull.call(builder,column)}if(predicateColumn===void 0){predicateColumn=column;predicateOperator="IS NULL";predicateValue=void 0;predicateParameterized=!1;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"IS NULL",value:void 0,parameterized:!1});return proxy},whereNotNull(column){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNotNull.call(builder,column)}if(predicateColumn===void 0){predicateColumn=column;predicateOperator="IS NOT NULL";predicateValue=void 0;predicateParameterized=!1;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"IS NOT NULL",value:void 0,parameterized:!1});return proxy},whereLike(column,value,caseSensitive=!1){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereLike.call(builder,column,value,caseSensitive)}if(!caseSensitive)return proxy.whereILike(column,value);if(predicateColumn===void 0){predicateColumn=column;predicateOperator="LIKE";predicateValue=value;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"LIKE",value,parameterized:!0});return proxy},whereNotLike(column,value,caseSensitive=!1){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNotLike.call(builder,column,value,caseSensitive)}if(!caseSensitive)return proxy.whereNotILike(column,value);if(predicateColumn===void 0){predicateColumn=column;predicateOperator="NOT LIKE";predicateValue=value;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"NOT LIKE",value,parameterized:!0});return proxy},whereILike(column,value){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereILike.call(builder,column,value)}const sqlColumn=`LOWER(${column})`;if(predicateColumn===void 0){predicateColumn=sqlColumn;predicateOperator="LIKE LOWER";predicateValue=void 0;predicateParameterized=!1;predicateValues=[value]}else(additionalPredicates??=[]).push({column:sqlColumn,operator:"LIKE LOWER",value:void 0,parameterized:!1,values:[value]});return proxy},whereNotILike(column,value){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNotILike.call(builder,column,value)}const sqlColumn=`LOWER(${column})`;if(predicateColumn===void 0){predicateColumn=sqlColumn;predicateOperator="NOT LIKE LOWER";predicateValue=void 0;predicateParameterized=!1;predicateValues=[value]}else(additionalPredicates??=[]).push({column:sqlColumn,operator:"NOT LIKE LOWER",value:void 0,parameterized:!1,values:[value]});return proxy},whereIn(column,values){if(typeof column!=="string"||!isSimpleSqliteColumn(column)||!Array.isArray(values)){const builder=materialize();return builder.whereIn.call(builder,column,values)}const snapshot=values.slice();if(predicateColumn===void 0){predicateColumn=column;predicateOperator="IN";predicateValue=void 0;predicateParameterized=!1;predicateValues=snapshot}else(additionalPredicates??=[]).push({column,operator:"IN",value:void 0,parameterized:!1,values:snapshot});return proxy},whereNotIn(column,values){if(typeof column!=="string"||!isSimpleSqliteColumn(column)||!Array.isArray(values)){const builder=materialize();return builder.whereNotIn.call(builder,column,values)}const snapshot=values.slice();if(predicateColumn===void 0){predicateColumn=column;predicateOperator="NOT IN";predicateValue=void 0;predicateParameterized=!1;predicateValues=snapshot}else(additionalPredicates??=[]).push({column,operator:"NOT IN",value:void 0,parameterized:!1,values:snapshot});return proxy},whereBetween(...args){const[column,startOrValues,end]=args,values=Array.isArray(startOrValues)?startOrValues:args.length>=3?[startOrValues,end]:void 0;if(typeof column!=="string"||!isSimpleSqliteColumn(column)||!values||values.length<2){const builder=materialize();return builder.whereBetween.call(builder,...args)}const lower=values[0],upper=values[1];if(predicateColumn===void 0){predicateColumn=column;predicateOperator=">=";predicateValue=lower;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:">=",value:lower,parameterized:!0});(additionalPredicates??=[]).push({column,operator:"<=",value:upper,parameterized:!0});return proxy},whereDate(...args){const[column,operator,date]=args;if(typeof column!=="string"||!isSimpleSqliteColumn(column)||typeof operator!=="string"||!SIMPLE_SQLITE_OPERATORS.has(operator.toLowerCase())||typeof date!=="string"&&!(date instanceof Date)){const builder=materialize();return builder.whereDate.call(builder,...args)}const normalizedDate=date instanceof Date?date.toISOString():date;if(predicateColumn===void 0){predicateColumn=column;predicateOperator=operator;predicateValue=normalizedDate;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator,value:normalizedDate,parameterized:!0});return proxy},orderBy(column,direction="asc"){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.orderBy.call(builder,column,direction)}(orderings??=[]).push({column,direction:direction==="asc"?"asc":"desc"});return proxy},orderByDesc(column){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.orderByDesc.call(builder,column)}(orderings??=[]).push({column,direction:"desc"});return proxy},latest(column){const resolvedColumn=column??queryBuilderConfig.timestamps.defaultOrderColumn;if(typeof resolvedColumn!=="string"||!isSimpleSqliteColumn(resolvedColumn)){const builder=materialize();return builder.latest.call(builder,column)}(orderings??=[]).push({column:resolvedColumn,direction:"desc"});return proxy},oldest(column){const resolvedColumn=column??queryBuilderConfig.timestamps.defaultOrderColumn;if(typeof resolvedColumn!=="string"||!isSimpleSqliteColumn(resolvedColumn)){const builder=materialize();return builder.oldest.call(builder,column)}(orderings??=[]).push({column:resolvedColumn,direction:"asc"});return proxy},offset(value){if(typeof value!=="number"||value<0||!Number.isInteger(value)){const builder=materialize();return builder.offset.call(builder,value)}rowOffset=value;return proxy}});let extensions;const base={select(value){if(materialized)return Reflect.apply(materialized.select,materialized,arguments);if(typeof value==="string"){if(value!=="*"&&!isSimpleSqliteSelection(value)){const builder=materialize();return builder.select.call(builder,value)}columns=value;selectedColumnsSql=value;return proxy}const selected=Array.isArray(value)?value:[value];let simple=!1,selection;const cached=lastSqliteSelection;if(cached&&cached.columns.length===selected.length){simple=!0;for(let index=0;simple&&index<selected.length;index++)simple=cached.columns[index]===selected[index];if(simple)selection=cached.sql}if(!simple){simple=selected.length>0;selection="";for(let index=0;simple&&index<selected.length;index++){const column=selected[index];simple=typeof column==="string"&&(column==="*"||isSimpleSqliteSelection(column));if(simple)selection+=index===0?column:`, ${column}`}if(simple)lastSqliteSelection={columns:selected.slice(),sql:selection}}if(!simple){const builder=materialize();return builder.select.call(builder,value)}columns=selected;selectedColumnsSql=selection;return proxy},where(column,operator,value){if(materialized)return Reflect.apply(materialized.where,materialized,arguments);if(typeof column!=="string"||!isSimpleSqliteColumn(column)||typeof operator!=="string"||operator!=="="&&!SIMPLE_SQLITE_OPERATORS.has(operator)&&!SIMPLE_SQLITE_OPERATORS.has(operator.toLowerCase())){if(typeof column==="string"&&isSimpleSqliteColumn(column)&&typeof operator==="string"){const normalized=operator.toLowerCase();if(normalized==="in"||normalized==="not in"){const values=Array.isArray(value)?snapshotSimpleSqliteMembershipValues(value):[value];if(values!==void 0){const membershipOperator=normalized==="in"?"IN":"NOT IN";if(predicateColumn===void 0){predicateColumn=column;predicateOperator=membershipOperator;predicateValue=void 0;predicateParameterized=!1;predicateValues=values}else(additionalPredicates??=[]).push({column,operator:membershipOperator,value:void 0,parameterized:!1,values});return proxy}}}return(extensions??=createExtensions()).where(column,operator,value)}if(predicateColumn===void 0){predicateColumn=column;predicateOperator=operator;predicateValue=value;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator,value,parameterized:!0});return proxy},limit(value){if(materialized)return Reflect.apply(materialized.limit,materialized,arguments);if(typeof value!=="number"||value<0||!Number.isInteger(value)){const builder=materialize();return builder.limit.call(builder,value)}rowLimit=value;return proxy},execute(){if(materialized)return Reflect.apply(materialized.execute,materialized,arguments);try{return Promise.resolve(executeStatement())}catch(error){return Promise.reject(error)}},executeSync(){if(materialized)return Reflect.apply(materialized.executeSync,materialized,arguments);return executeStatement()},executeTakeFirstSync(){if(materialized)return Reflect.apply(materialized.executeTakeFirstSync,materialized,arguments);return executeFirstStatement()}};let forwardedMethods;proxy=new Proxy(base,{get(target,property){if(materialized){const builder=materialized,value=builder[property];return typeof value==="function"?value.bind(builder):value}if(property==="executeTakeFirst")return target.executeTakeFirst??=function(){if(materialized)return Reflect.apply(materialized.executeTakeFirst,materialized,arguments);try{return Promise.resolve(executeFirstStatement())}catch(error){return Promise.reject(error)}};if(GUARDED_SQLITE_SELECT_METHODS.has(property))return target[property];const cached=forwardedMethods?.[property];if(cached!==void 0)return cached;const value=target[property]??resolveDeferredSqliteTerminal(target,property,executeStatement,executeFirstStatement,materialize,proxy)??(extensions??=createExtensions())[property];if(value===void 0){const builder=materialize(),fallback=builder[property];return typeof fallback==="function"?fallback.bind(builder):fallback}if(typeof value!=="function")return value;const forward=(...args)=>{const builder=materialized;return builder?Reflect.apply(builder[property],builder,args):Reflect.apply(value,proxy,args)};(forwardedMethods??=Object.create(null))[property]=forward;return forward}});return proxy}function usesDeferredSqliteSelect(table){return getDialect()==="sqlite"&&!isLibsqlDriver(getDriver())&&!queryBuilderConfig.softDeletes?.enabled&&!hasActiveQueryBuilderHooks()&&isSimpleSqliteTable(table)}function selectFromDatabase(table){const instance=getDialect()==="sqlite"?getDb():getReadDb();if(usesDeferredSqliteSelect(table))return guardTransactionQuery(createDeferredSqliteSelect(instance,table));return guardTransactionQuery(instance.selectFrom(table))}function selectFromExplicitReadDatabase(table){const instance=getExplicitReadDb();if(usesDeferredSqliteSelect(table))return guardTransactionQuery(createDeferredSqliteSelect(instance,table));return guardTransactionQuery(instance.selectFrom(table))}function unsafeDatabase(query,params){return guardTransactionQuery(getDb().unsafe(query,params))}function unsafeExplicitReadDatabase(query,params){return guardTransactionQuery(getExplicitReadDb().unsafe(query,params))}function insertIntoDatabase(table){markContextWrote();return guardTransactionQuery(getDb().insertInto(table))}function updateTableDatabase(table){markContextWrote();return guardTransactionQuery(getDb().updateTable(table))}function deleteFromDatabase(table){markContextWrote();return guardTransactionQuery(getDb().deleteFrom(table))}function tableDatabase(table){return guardTransactionQuery(getDb().table(table))}function selectDatabase(table,...columns){return guardTransactionQuery(getReadDb().select(table,...columns))}function selectFromSubDatabase(subquery,alias){return guardTransactionQuery(getReadDb().selectFromSub(subquery,alias))}function tableExplicitReadDatabase(table){return guardTransactionQuery(getExplicitReadDb().table(table))}function selectExplicitReadDatabase(table,...columns){return guardTransactionQuery(getExplicitReadDb().select(table,...columns))}function selectFromSubExplicitReadDatabase(subquery,alias){return guardTransactionQuery(getExplicitReadDb().selectFromSub(subquery,alias))}function transactionalDatabase(callback,options){return(...args)=>getDb().transaction((tx)=>callback(tx,...args),options)}const dbFallback=new Proxy({},{get(_target,prop){const resolveInstance=typeof prop==="string"&&READ_ENTRY_POINTS.has(prop)?getReadDb:getDb,value=resolveInstance()[prop];if(typeof value==="function")return forwardDatabaseMethod(prop,resolveInstance);return value}});function forwardDatabaseMethod(prop,resolveInstance){const scope=transactionConnection.getStore();return(...args)=>{if(scope){assertTransactionConnection(scope);if(transactionConnection.getStore()!==scope)throw Error("A transaction query must execute in the callback that created it.")}const instance=resolveInstance();if(typeof prop==="string"&&WRITE_ENTRY_POINTS.has(prop))markContextWrote();return guardTransactionQuery(Reflect.apply(Reflect.get(instance,prop),instance,args))}}export const db=Object.create(dbFallback);Object.defineProperties(db,{deleteFrom:{value:deleteFromDatabase},fn:{value:aggregateFunctions},insertInto:{value:insertIntoDatabase},primary:{get:()=>primaryDb},read:{get:()=>readDb},select:{value:selectDatabase},selectFrom:{value:selectFromDatabase},selectFromSub:{value:selectFromSubDatabase},table:{value:tableDatabase},transactional:{value:transactionalDatabase},unsafe:{value:unsafeDatabase},updateTable:{value:updateTableDatabase}});const readDbFallback=new Proxy({},{get(_target,prop){const value=getExplicitReadDb()[prop];if(typeof value==="function")return forwardDatabaseMethod(prop,getExplicitReadDb);return value}});export const readDb=Object.create(readDbFallback);Object.defineProperties(readDb,{fn:{value:aggregateFunctions},select:{value:selectExplicitReadDatabase},selectFrom:{value:selectFromExplicitReadDatabase},selectFromSub:{value:selectFromSubExplicitReadDatabase},table:{value:tableExplicitReadDatabase},transactional:{value:transactionalDatabase},unsafe:{value:unsafeExplicitReadDatabase}});const primaryDb=new Proxy({},{get(_target,prop){const instance=getDb(),value=Reflect.get(instance,prop);return typeof value==="function"?forwardDatabaseMethod(prop,getDb):value}});export{setConfig};
|
|
1
|
+
import{AsyncLocalStorage}from"node:async_hooks";import{isPromise,isProxy}from"node:util/types";import{config as queryBuilderConfig,createQueryBuilder,registerPersistentQueryHooks,resetConnection as resetQueryBuilderConnection,setConfig}from"@stacksjs/query-builder";import{SQL}from"bun";import{closeDatabaseConnectionsAndPending,retireDatabaseConnections}from"./connection-lifecycle";import{runInTransactionScope}from"./transaction-context";import{env as envVars}from"@stacksjs/env/runtime";import{getConnectionDefaults}from"./defaults";import{isLibsqlDriver,isMysqlWire,isVitessSharded,normalizeDatabaseDriver,setConfiguredDatabaseDriver,toQueryBuilderDialect}from"./dialect";import{relativeMigrationDirectory,resolveMigrationDirectory,snapshotDirForQueryBuilder}from"./migration-path";import{configureDatabaseRoutingContext,contextInTransaction,markContextWrote,replicaConnectionKey,resolveReplicaConnection,runInDatabaseRoutingContext,selectReplica,shouldRouteToReplica,withDatabaseRoutingContext,withTransactionContext}from"./replicas";import{aggregateFunctions}from"./types";const sqliteDefaults=getConnectionDefaults("sqlite",envVars),mysqlDefaults=getConnectionDefaults("mysql",envVars),postgresDefaults=getConnectionDefaults("postgres",envVars);let appEnv=envVars.APP_ENV||"local",dbDriver=normalizeDatabaseDriver(envVars.DB_CONNECTION||"sqlite"),queryBuilderDialect=toQueryBuilderDialect(dbDriver),queryLoggingEnabled=envVars.DB_QUERY_LOGGING_ENABLED??!isProductionEnvironment(appEnv);const envDbConfig=()=>({connections:{sqlite:{database:sqliteDefaults.database,prefix:""},mysql:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},singlestore:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},vitess:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:15306,prefix:"",sharded:isVitessSharded()},postgres:{name:postgresDefaults.database,host:postgresDefaults.host,username:postgresDefaults.username,password:postgresDefaults.password,port:postgresDefaults.port,prefix:""},turso:{url:envVars.TURSO_DATABASE_URL||"",authToken:envVars.TURSO_AUTH_TOKEN||"",prefix:""}}});let dbConfig=envDbConfig(),dbConfigLockTail=Promise.resolve();const DB_CONFIG_LOCK_MAX_HOLD_MS=60000;export function acquireDbConfigLock(){let release=()=>{};const held=new Promise((resolve)=>{release=resolve}),acquired=dbConfigLockTail.then(()=>{const watchdog=setTimeout(()=>{console.warn(`[database] the db config lock was held for over ${DB_CONFIG_LOCK_MAX_HOLD_MS/1000}s. Releasing it so the queue can proceed - a test file most likely failed before its afterAll ran.`);release()},DB_CONFIG_LOCK_MAX_HOLD_MS);watchdog.unref?.();held.then(()=>clearTimeout(watchdog));return release});dbConfigLockTail=dbConfigLockTail.then(()=>held).catch(()=>{});return acquired}export function initializeDbConfig(config){if(config?.app?.env)appEnv=config.app.env;if(config?.database?.default){dbDriver=normalizeDatabaseDriver(config.database.default);setConfiguredDatabaseDriver(dbDriver)}queryBuilderDialect=toQueryBuilderDialect(dbDriver);if(config?.database?.connections){const baseline=envDbConfig(),supplied=config.database.connections,connections={...baseline.connections};for(const[name,connection]of Object.entries(supplied))if(connection&&typeof connection==="object")connections[name]={...connections[name]??{},...connection};dbConfig={...config.database,connections}}configureDatabaseRoutingContext(getReplicas().length>0);queryLoggingEnabled=config?.database?.queryLogging?.enabled??envVars.DB_QUERY_LOGGING_ENABLED??!isProductionEnvironment(appEnv);syncDatabaseQueryHooks();updateQueryBuilderConfig();_dbInstance=null;_replicaInstances=new Map}function getEnv(){return appEnv}function isProductionEnvironment(value){return value==="production"||value==="prod"}function getDriver(){return dbDriver}function getDatabaseConfig(){return dbConfig}export function resolvedDatabaseConfig(){return{driver:dbDriver,connections:dbConfig.connections}}function getDialect(){return queryBuilderDialect}export{getDialect as getDatabaseDialect};function getDbConfig(){return{url:void 0,authToken:void 0,...getDriverDbConfig()}}function getDriverDbConfig(){const driver=getDriver(),database=getDatabaseConfig(),env=getEnv();if(isLibsqlDriver(driver)){const turso=database.connections?.turso,url=turso?.url||envVars.TURSO_DATABASE_URL||"";if(!url)throw Error("[database] DB_CONNECTION=turso needs the database URL: set TURSO_DATABASE_URL (libsql://<db>-<org>.turso.io, or http://127.0.0.1:8080 for `turso dev`) or connections.turso.url in config/database.ts.");return{database:url,url,authToken:turso?.authToken||envVars.TURSO_AUTH_TOKEN||void 0}}if(driver==="sqlite"){const defaultName=env!=="testing"?"database/stacks.sqlite":"database/stacks_testing.sqlite";return{database:database.connections?.sqlite?.database??defaultName}}if(driver==="mysql")return{database:database.connections?.mysql?.name||"stacks",host:database.connections?.mysql?.host??"127.0.0.1",username:database.connections?.mysql?.username??"root",password:database.connections?.mysql?.password??"",port:database.connections?.mysql?.port??3306};if(driver==="singlestore")return{database:database.connections?.singlestore?.name||"stacks",host:database.connections?.singlestore?.host??"127.0.0.1",username:database.connections?.singlestore?.username??"root",password:database.connections?.singlestore?.password??"",port:database.connections?.singlestore?.port??3306};if(driver==="vitess")return{database:database.connections?.vitess?.name||"stacks",host:database.connections?.vitess?.host??"127.0.0.1",username:database.connections?.vitess?.username??"root",password:database.connections?.vitess?.password??"",port:database.connections?.vitess?.port??15306};if(driver==="postgres"){const dbName=database.connections?.postgres?.name??"stacks";return{database:env==="testing"?`${dbName}_testing`:dbName,host:database.connections?.postgres?.host??"127.0.0.1",username:database.connections?.postgres?.username??"",password:database.connections?.postgres?.password??"",port:database.connections?.postgres?.port??5432}}return{database:":memory:"}}export const QB_SNAPSHOT_DIR="storage/framework/database";export function qbSnapshotDir(){return snapshotDirForQueryBuilder()}let migrationDirOrigin;export function qbMigrationDir(){migrationDirOrigin??={value:queryBuilderConfig.migrationDir};return migrationDirOrigin.value}export const RAW_QUERY_SOFT_DELETE_CONFIG={enabled:!1,column:"deleted_at",defaultFilter:!0};export function createDatabaseQueryHooks(dispatch){function forward(event){try{Promise.resolve(dispatch(event)).catch(()=>{})}catch{}}return{onQueryEnd:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs}),onQueryError:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs,error:event.error})}}let queryLoggerModule;function forwardDatabaseQuery(event){transactionConnection.exit(()=>{queryLoggerModule??=import("./query-logger").catch((error)=>{queryLoggerModule=void 0;throw error});queryLoggerModule.then(({logQuery})=>logQuery(event)).catch(()=>{})})}let unregisterDatabaseQueryHooks;function syncDatabaseQueryHooks(){const shouldInstall=!isProductionEnvironment(appEnv)||queryLoggingEnabled;if(shouldInstall&&!unregisterDatabaseQueryHooks)unregisterDatabaseQueryHooks=registerPersistentQueryHooks(createDatabaseQueryHooks(forwardDatabaseQuery));else if(!shouldInstall&&unregisterDatabaseQueryHooks){unregisterDatabaseQueryHooks();unregisterDatabaseQueryHooks=void 0}}syncDatabaseQueryHooks();function getPoolConfig(){const driver=getDriver();if(driver==="sqlite"||isLibsqlDriver(driver))return;return getDatabaseConfig().connections?.[driver]?.pool}const EMPTY_REPLICAS=[];function getReplicas(){const driver=getDriver();if(driver==="sqlite"||isLibsqlDriver(driver))return EMPTY_REPLICAS;return getDatabaseConfig().connections?.[driver]?.replicas??EMPTY_REPLICAS}function getReadPolicy(){return getDatabaseConfig().reads??{}}export{runInDatabaseRoutingContext,withDatabaseRoutingContext};function toBunPoolOptions(pool){if(!pool)return{};const options={};if(pool.max!==void 0)options.max=pool.max;if(pool.idleTimeoutMs!==void 0)options.idleTimeout=Math.round(pool.idleTimeoutMs/1000);if(pool.acquireTimeoutMs!==void 0)options.connectionTimeout=Math.round(pool.acquireTimeoutMs/1000);if(pool.maxLifetimeMs!==void 0)options.maxLifetime=Math.round(pool.maxLifetimeMs/1000);if(pool.preparedStatements!==void 0)options.prepare=pool.preparedStatements;return options}function updateQueryBuilderConfig(){const dialect=getDialect(),dbConfigForQb=getDbConfig(),pool=getPoolConfig();setConfig({dialect,vitess:{sharded:isVitessSharded(dbConfig.connections.vitess?.sharded)},database:pool?{...dbConfigForQb,pool}:dbConfigForQb,verbose:getEnv()!=="production",snapshotDir:qbSnapshotDir(),migrationDir:relativeMigrationDirectory(resolveMigrationDirectory(toQueryBuilderDialect(dialect),{configured:qbMigrationDir(),snapshotDir:qbSnapshotDir()})),timestamps:{createdAt:"created_at",updatedAt:"updated_at",defaultOrderColumn:"created_at"},softDeletes:RAW_QUERY_SOFT_DELETE_CONFIG})}updateQueryBuilderConfig();let _dbInstance=null,_configInitPromise=null;function ensureConfigLoaded(){if(!_configInitPromise)_configInitPromise=(async()=>{try{const{config,overridesReady}=await import("@stacksjs/config/runtime");await overridesReady;if(config){initializeDbConfig(config);_dbInstance=null}}catch{}})();return _configInitPromise}export async function ensureDatabaseConfigLoaded(){await ensureConfigLoaded()}export{applySqlitePragmas,SQLITE_BOOTSTRAP_PRAGMAS}from"@stacksjs/query-builder";const sqliteTxOwner=new AsyncLocalStorage;let sqliteTxTail=Promise.resolve();function runOwnedSqliteTransaction(run,parent){const owner={parent,accepting:!0,transactions:0,completionTail:Promise.resolve()};return sqliteTxOwner.run(owner,async()=>{try{return await run()}finally{owner.accepting=!1;await owner.completionTail}})}function serializeSqliteTransaction(run){let owner=sqliteTxOwner.getStore();while(owner&&!owner.accepting)owner=owner.parent;if(owner?.accepting&&owner.transactions>0)return sqliteTxOwner.run(owner,run);const completionOwner=owner?.accepting?owner:void 0,result=(completionOwner?.completionTail??sqliteTxTail).then(()=>runOwnedSqliteTransaction(run,completionOwner)),tail=result.then(()=>{return},()=>{return});if(completionOwner)completionOwner.completionTail=tail;else sqliteTxTail=tail;return result}function applySqliteTransactionSerialization(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>serializeSqliteTransaction(()=>original(...args))}const dispatchScopedBuilders=new WeakSet,transactionConnection=new AsyncLocalStorage;function activeTransactionConnection(){const scope=transactionConnection.getStore();if(!scope)return;assertTransactionConnection(scope);return scope.connection}function assertTransactionConnection(scope){for(let owner=scope;owner;owner=owner.parent)if(!owner.active)throw Error("Database work cannot continue from a closed transaction callback.");if(scope.nested)throw Error("Await the active nested transaction before issuing more database work.")}function guardTransactionQuery(value){const scope=transactionConnection.getStore();if(!scope||!value||typeof value!=="object"||typeof value.execute!=="function")return value;const wrappers=new WeakMap,wrap=(target)=>{const cached=wrappers.get(target);if(cached)return cached;const proxy=new Proxy(target,{get(object,property){const member=Reflect.get(object,property,object);if(typeof member!=="function")return member;return(...args)=>{assertTransactionConnection(scope);if(transactionConnection.getStore()!==scope)throw Error("A transaction query must execute in the callback that created it.");const guardedArgs=args.map((arg)=>typeof arg==="function"?function(...values){return Reflect.apply(arg,this,values.map((value)=>value&&typeof value==="object"&&typeof Reflect.get(value,"execute")==="function"?wrap(value):value))}:arg),result=Reflect.apply(member,object,guardedArgs);if(result===object)return proxy;if(result&&typeof result==="object"&&!isPromise(result)&&(typeof Reflect.get(result,"execute")==="function"||typeof Reflect.get(result,"next")==="function"))return wrap(result);return result}}});wrappers.set(target,proxy);return proxy};return wrap(value)}function applyTransactionDispatchScope(instance){if(dispatchScopedBuilders.has(instance))return;dispatchScopedBuilders.add(instance);let defaultOptions={};const setDefaults=instance.setTransactionDefaults.bind(instance);instance.setTransactionDefaults=(defaults)=>{setDefaults(defaults);defaultOptions={...defaultOptions,...defaults}};for(const method of["transaction","savepoint"]){const original=instance[method].bind(instance);instance[method]=async(callback,options)=>{const active=activeTransactionConnection();if(active&&active!==instance)return active[method](callback,options);const parent=transactionConnection.getStore(),effectiveOptions={...defaultOptions,...options},afterCommit=method==="transaction"?effectiveOptions.afterCommit:void 0,parentObserver=(listener)=>listener?(...args)=>{const nested=parent?.nested;if(parent)parent.nested=!1;try{listener(...args)}finally{if(parent)parent.nested=nested}}:void 0,result=await runInTransactionScope(async(runAttempt)=>{const owner=sqliteTxOwner.getStore();if(owner)owner.transactions++;if(parent)parent.nested=!0;try{const runCallback=AsyncLocalStorage.bind((tx)=>runAttempt(async()=>{applyTransactionDispatchScope(tx);const scope={connection:tx,parent,active:!0,nested:!1};return transactionConnection.run(scope,async()=>{try{return await callback(tx)}finally{scope.active=!1}})}));return await original(runCallback,method==="transaction"?{...options,afterCommit:void 0,onRollback:parentObserver(effectiveOptions.onRollback),afterRollback:parentObserver(effectiveOptions.afterRollback),onRetry:parentObserver(effectiveOptions.onRetry)}:options)}finally{if(parent)parent.nested=!1;if(owner)owner.transactions--}});await afterCommit?.();return result}}}function applyTransactionRoutingContext(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>withTransactionContext(()=>original(...args))}function getDb(){if(_databaseClosePromise)throw Error("Database connections are shutting down");const active=activeTransactionConnection();if(active)return active;if(!_dbInstance){updateQueryBuilderConfig();_dbInstance=createQueryBuilder();applyTransactionDispatchScope(_dbInstance);if(getDialect()==="sqlite")applySqliteTransactionSerialization(_dbInstance);applyTransactionRoutingContext(_dbInstance)}return _dbInstance}let _replicaInstances=new Map,_databaseClosePromise=null;const _retiredConnectionClosures=new Set;export function resetDatabaseConnection(){const connections=[..._dbInstance?[_dbInstance]:[],..._replicaInstances.values()];if(connections.length>0)retireDatabaseConnections(_retiredConnectionClosures,connections,(error)=>console.error(`[database] Failed to close retired connection: ${error.message}`));resetQueryBuilderConnection();lastGeneralSqliteStatement=void 0;_dbInstance=null;_replicaInstances=new Map}export function closeDatabaseConnection(){if(_databaseClosePromise)return _databaseClosePromise;const connections=[..._dbInstance?[_dbInstance]:[],..._replicaInstances.values()];_dbInstance=null;_replicaInstances=new Map;lastGeneralSqliteStatement=void 0;const closing=(async()=>{try{await closeDatabaseConnectionsAndPending(connections,_retiredConnectionClosures)}finally{resetQueryBuilderConnection()}})();_databaseClosePromise=closing;closing.then(()=>{if(_databaseClosePromise===closing)_databaseClosePromise=null},()=>{if(_databaseClosePromise===closing)_databaseClosePromise=null});return closing}function getReplicaDb(replica){const primary=getDbConfig(),resolved=resolveReplicaConnection(replica,primary),key=replicaConnectionKey(resolved),cached=_replicaInstances.get(key);if(cached)return cached;const scheme=isMysqlWire(getDriver())?"mysql":"postgres",auth=resolved.username?`${encodeURIComponent(resolved.username)}:${encodeURIComponent(resolved.password??"")}@`:"",url=`${scheme}://${auth}${resolved.host}:${resolved.port}/${resolved.database}`,sql=new SQL(url,{...toBunPoolOptions(getPoolConfig()),database:resolved.database}),instance=createQueryBuilder({sql});_replicaInstances.set(key,instance);return instance}function getReadDb(){const active=activeTransactionConnection();if(active)return active;const replicas=getReplicas();if(replicas.length===0)return getDb();const policy=getReadPolicy();if(!shouldRouteToReplica({policy,replicas}))return getDb();const replica=selectReplica(replicas,policy.strategy);return replica?getReplicaDb(replica):getDb()}function getExplicitReadDb(){const active=activeTransactionConnection();if(active)return active;const replicas=getReplicas();if(!replicas.length||contextInTransaction())return getDb();const replica=selectReplica(replicas,getReadPolicy().strategy);return replica?getReplicaDb(replica):getDb()}const WRITE_ENTRY_POINTS=new Set(["insertInto","updateTable","deleteFrom","create","createMany","insertOrIgnore","insertGetId","updateOrInsert","upsert"]),READ_ENTRY_POINTS=new Set(["selectFrom","selectFromSub","select"]);ensureConfigLoaded();export function asRows(rows){return rows}export function asRow(row){return row}const SIMPLE_SQLITE_TABLE=/^[A-Z_][A-Z0-9_]*$/i,SIMPLE_SQLITE_COLUMN=/^[A-Z_][A-Z0-9_]*(?:\.[A-Z_][A-Z0-9_]*)?$/i,SIMPLE_SQLITE_SELECTION=/^[A-Z_][A-Z0-9_]*(?:\.[A-Z_][A-Z0-9_]*)?(?:\s+AS\s+[A-Z_][A-Z0-9_]*)?$/i,SIMPLE_SQLITE_OPERATORS=new Set(["=","!=","<>","<","<=",">",">=","like","not like"]),SQLITE_IDENTIFIER_CACHE_LIMIT=512;function memoizeSqliteIdentifier(pattern){const valid=new Set;return(value)=>{if(valid.has(value))return!0;if(!pattern.test(value))return!1;if(valid.size<SQLITE_IDENTIFIER_CACHE_LIMIT)valid.add(value);return!0}}const isSimpleSqliteTable=memoizeSqliteIdentifier(SIMPLE_SQLITE_TABLE),isSimpleSqliteColumn=memoizeSqliteIdentifier(SIMPLE_SQLITE_COLUMN),isSimpleSqliteSelection=memoizeSqliteIdentifier(SIMPLE_SQLITE_SELECTION);let lastParameterizedSqliteSelect,lastUnparameterizedSqliteSelect,lastSqliteSelection;const GUARDED_SQLITE_SELECT_METHODS=new Set(["select","where","limit","execute","executeSync","executeTakeFirstSync"]);function hasActiveQueryBuilderHooks(){return Boolean(queryBuilderConfig.hooks&&Object.values(queryBuilderConfig.hooks).some((value)=>value!==void 0))}function resolveDeferredSqliteTerminal(target,property,executeStatement,executeFirstStatement,materialize,proxy){if(property==="get"){target.get=target.execute;return target.execute}if(property==="selectAll"){const selectAll=()=>proxy;target.selectAll=selectAll;return selectAll}if(property==="first"){const first=async()=>executeFirstStatement();target.first=first;return first}if(property==="firstOrFail"||property==="executeTakeFirstOrThrow"){const firstOrFail=async()=>{const row=executeFirstStatement();if(row===void 0)throw Error("Record not found");return row};target.firstOrFail=firstOrFail;target.executeTakeFirstOrThrow=firstOrFail;return firstOrFail}if(property==="exists"||property==="doesntExist"){const findsRow=property==="exists",check=async()=>executeFirstStatement()!==void 0===findsRow;target[property]=check;return check}if(property==="value"){const value=async(column)=>executeFirstStatement()?.[column];target.value=value;return value}if(property==="count"||property==="sum"||property==="avg"||property==="min"||property==="max"){const emptyValue=property==="min"||property==="max"?null:0,aggregate=(...args)=>runDeferredSqliteAggregate(property,args,emptyValue,materialize,executeStatement);target[property]=aggregate;return aggregate}if(property==="pluck"){const pluck=(...args)=>{if(args.length!==1){const builder=materialize();return builder.pluck.call(builder,...args)}try{const column=args[0],rows=executeStatement(),values=Array(rows.length);for(let index=0;index<rows.length;index++)values[index]=rows[index]?.[column];return Promise.resolve(values)}catch(error){return Promise.reject(error)}};target.pluck=pluck;return pluck}}function runDeferredSqliteAggregate(name,args,emptyValue,materialize,executeStatement){const column=args[0],acceptsNoColumn=name==="count"&&args.length===0;if(!acceptsNoColumn&&(args.length!==1||typeof column!=="string"||!isSimpleSqliteColumn(column))){const builder=materialize();return builder[name].call(builder,...args)}try{const expression=`${name.toUpperCase()}(${acceptsNoColumn?"*":column}) AS aggregate`,value=executeStatement(!1,expression)[0]?.aggregate??emptyValue;return Promise.resolve(name==="count"||name==="sum"||name==="avg"?Number(value):value)}catch(error){return Promise.reject(error)}}const fastSqliteDatabaseCache=new WeakMap;let lastGeneralSqliteStatement;function fastSqliteDatabase(instance){if(fastSqliteDatabaseCache.has(instance))return fastSqliteDatabaseCache.get(instance)??void 0;const database=instance.sql?._wrapper?.database,resolved=database&&typeof database.query==="function"?database:null;fastSqliteDatabaseCache.set(instance,resolved);return resolved??void 0}function runFastSqliteSql(instance,sqliteDatabase,sql,params){if(sqliteDatabase){let cached=lastGeneralSqliteStatement;if(!cached||cached.database!==sqliteDatabase||cached.sql!==sql){cached={database:sqliteDatabase,sql,statement:sqliteDatabase.query(sql)};lastGeneralSqliteStatement=cached}return params?cached.statement.all(...params):cached.statement.all()}return instance.unsafe(sql,params).executeSync()}let lastSqlitePlaceholders;function renderSqlitePlaceholders(length){const cached=lastSqlitePlaceholders;if(cached&&cached.length===length)return cached.sql;const sql=Array(length).fill("?").join(", ");if(typeof length==="number")lastSqlitePlaceholders={length,sql};return sql}function snapshotSimpleSqliteMembershipValues(values){if(isProxy(values)||Object.getPrototypeOf(values)!==Array.prototype||Object.hasOwn(values,Symbol.iterator))return;const snapshot=Array(values.length);for(let index=0;index<values.length;index++){const descriptor=Object.getOwnPropertyDescriptor(values,index);if(!descriptor||!("value"in descriptor))return;snapshot[index]=descriptor.value}return snapshot}function createDeferredSqliteSelect(instance,table){const sqliteDatabase=fastSqliteDatabase(instance);let selectKeyword="SELECT",columns,selectedColumnsSql,predicateColumn,predicateOperator,predicateValue,predicateParameterized=!0,predicateValues,additionalPredicates,orderings,rowLimit,rowOffset,materialized;const materialize=()=>{if(materialized)return materialized;let builder=instance.selectFrom(table);if(columns)builder=builder.select.call(builder,columns);if(selectKeyword==="SELECT DISTINCT")builder=builder.distinct.call(builder);if(predicateColumn!==void 0){if(predicateValues)if(predicateOperator==="LIKE LOWER"||predicateOperator==="NOT LIKE LOWER")builder=builder[predicateOperator==="LIKE LOWER"?"whereILike":"whereNotILike"].call(builder,predicateColumn.slice(6,-1),predicateValues[0]);else builder=builder[predicateOperator==="IN"?"whereIn":"whereNotIn"].call(builder,predicateColumn,predicateValues);else if(predicateParameterized)builder=builder.where.call(builder,predicateColumn,predicateOperator,predicateValue);else builder=builder[predicateOperator==="IS NULL"?"whereNull":"whereNotNull"].call(builder,predicateColumn);if(additionalPredicates)for(const predicate of additionalPredicates)if(predicate.values)if(predicate.operator==="LIKE LOWER"||predicate.operator==="NOT LIKE LOWER"){const method=predicate.operator==="LIKE LOWER"?"whereILike":"whereNotILike";builder=builder[method].call(builder,predicate.column.slice(6,-1),predicate.values[0])}else{const method=predicate.operator==="IN"?"whereIn":"whereNotIn";builder=builder[method].call(builder,predicate.column,predicate.values)}else if(predicate.parameterized)builder=builder.where.call(builder,predicate.column,predicate.operator,predicate.value);else{const method=predicate.operator==="IS NULL"?"whereNull":"whereNotNull";builder=builder[method].call(builder,predicate.column)}}if(orderings){const apply=builder.orderBy;for(const ordering of orderings)builder=apply.call(builder,ordering.column,ordering.direction)}if(rowLimit!==void 0)builder=builder.limit.call(builder,rowLimit);if(rowOffset!==void 0)builder=builder.offset.call(builder,rowOffset);materialized=builder;return builder};let proxy;const executeStatement=(firstOnly=!1,selection)=>{const selected=selection??selectedColumnsSql??"*",effectiveLimit=rowLimit??(firstOnly&&rowOffset===void 0?1:void 0);if(predicateColumn===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastUnparameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.limit===effectiveLimit){if(sqliteDatabase){const statement=cached.statement??=sqliteDatabase.query(cached.sql);if(effectiveLimit===1){const row=statement.get();return row===null?[]:[row]}return statement.all()}return runFastSqliteSql(instance,sqliteDatabase,cached.sql)}const limit=effectiveLimit===void 0?"":` LIMIT ${effectiveLimit}`,sql=`${selectKeyword} ${selected} FROM ${table}${limit}`;lastUnparameterizedSqliteSelect={instance,selectKeyword,selected,table,limit:effectiveLimit,sql,statement:sqliteDatabase?.query(sql)};if(lastUnparameterizedSqliteSelect.statement){if(effectiveLimit===1){const row=lastUnparameterizedSqliteSelect.statement.get();return row===null?[]:[row]}return lastUnparameterizedSqliteSelect.statement.all()}return runFastSqliteSql(instance,sqliteDatabase,sql)}if(predicateColumn!==void 0&&predicateValues===void 0&&predicateParameterized&&additionalPredicates===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastParameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.predicateColumn===predicateColumn&&cached.predicateOperator===predicateOperator&&cached.limit===effectiveLimit){if(sqliteDatabase){const statement=cached.statement??=sqliteDatabase.query(cached.sql);if(effectiveLimit===1){const row=statement.get(predicateValue);return row===null?[]:[row]}return statement.all(predicateValue)}return runFastSqliteSql(instance,sqliteDatabase,cached.sql,[predicateValue])}const sql=`${selectKeyword} ${selected} FROM ${table} WHERE ${predicateColumn} ${predicateOperator} ?${effectiveLimit===void 0?"":` LIMIT ${effectiveLimit}`}`;lastParameterizedSqliteSelect={instance,selectKeyword,selected,table,predicateColumn,predicateOperator,limit:effectiveLimit,sql,statement:sqliteDatabase?.query(sql)};if(lastParameterizedSqliteSelect.statement){if(effectiveLimit===1){const row=lastParameterizedSqliteSelect.statement.get(predicateValue);return row===null?[]:[row]}return lastParameterizedSqliteSelect.statement.all(predicateValue)}return runFastSqliteSql(instance,sqliteDatabase,sql,[predicateValue])}let query=`${selectKeyword} ${selected} FROM ${table}`;const params=[];if(predicateColumn!==void 0){query+=` WHERE ${predicateColumn} ${predicateOperator}`;if(predicateValues){query+=` (${renderSqlitePlaceholders(predicateValues.length)})`;params.push(...predicateValues)}else if(predicateParameterized){query+=" ?";params.push(predicateValue)}if(additionalPredicates){const additionalCount=additionalPredicates.length;query+=" AND ";for(let index=0;index<additionalCount;index++){if(index>0)query+=" AND ";const predicate=additionalPredicates[index];if(predicate.values){params.push(...predicate.values);query+=`${predicate.column} ${predicate.operator} (${renderSqlitePlaceholders(predicate.values.length)})`;continue}if(predicate.parameterized){params.push(predicate.value);query+=`${predicate.column} ${predicate.operator} ?`;continue}query+=`${predicate.column} ${predicate.operator}`}}}if(orderings)query+=` ORDER BY ${orderings.map((ordering)=>`${ordering.column} ${ordering.direction.toUpperCase()}`).join(", ")}`;if(effectiveLimit!==void 0)query+=` LIMIT ${effectiveLimit}`;if(rowOffset!==void 0)query+=` OFFSET ${rowOffset}`;return runFastSqliteSql(instance,sqliteDatabase,query,params)},executeFirstStatement=()=>{if(rowLimit===0)return;const selected=selectedColumnsSql??"*";if(predicateColumn===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastUnparameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.limit===1){if(sqliteDatabase)return(cached.statement??=sqliteDatabase.query(cached.sql)).get()??void 0;return runFastSqliteSql(instance,sqliteDatabase,cached.sql)[0]}const sql=`${selectKeyword} ${selected} FROM ${table} LIMIT 1`,statement=sqliteDatabase?.query(sql);lastUnparameterizedSqliteSelect={instance,selectKeyword,selected,table,limit:1,sql,statement};return statement?statement.get()??void 0:runFastSqliteSql(instance,sqliteDatabase,sql)[0]}if(predicateColumn!==void 0&&predicateValues===void 0&&predicateParameterized&&additionalPredicates===void 0&&orderings===void 0&&rowOffset===void 0){const cached=lastParameterizedSqliteSelect;if(cached&&cached.instance===instance&&cached.selectKeyword===selectKeyword&&cached.selected===selected&&cached.table===table&&cached.predicateColumn===predicateColumn&&cached.predicateOperator===predicateOperator&&cached.limit===1){if(sqliteDatabase)return(cached.statement??=sqliteDatabase.query(cached.sql)).get(predicateValue)??void 0;return runFastSqliteSql(instance,sqliteDatabase,cached.sql,[predicateValue])[0]}const sql=`${selectKeyword} ${selected} FROM ${table} WHERE ${predicateColumn} ${predicateOperator} ? LIMIT 1`,statement=sqliteDatabase?.query(sql);lastParameterizedSqliteSelect={instance,selectKeyword,selected,table,predicateColumn,predicateOperator,limit:1,sql,statement};return statement?statement.get(predicateValue)??void 0:runFastSqliteSql(instance,sqliteDatabase,sql,[predicateValue])[0]}return executeStatement(!0)[0]},createExtensions=()=>({distinct(){if(selectKeyword==="SELECT DISTINCT")return materialize().distinct();selectKeyword="SELECT DISTINCT";return proxy},where(column,operator,value){if(column!==null&&typeof column==="object"&&!Array.isArray(column)&&operator===void 0&&value===void 0){const prototype=Object.getPrototypeOf(column),entries=prototype===Object.prototype||prototype===null?Object.entries(column):[];if(entries.length>0&&entries.every(([key])=>isSimpleSqliteColumn(key))){for(const[key,entryValue]of entries)if(predicateColumn===void 0){predicateColumn=key;predicateOperator="=";predicateValue=entryValue;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column:key,operator:"=",value:entryValue,parameterized:!0});return proxy}}const builder=materialize();return builder.where.call(builder,column,operator,value)},whereNull(column){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNull.call(builder,column)}if(predicateColumn===void 0){predicateColumn=column;predicateOperator="IS NULL";predicateValue=void 0;predicateParameterized=!1;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"IS NULL",value:void 0,parameterized:!1});return proxy},whereNotNull(column){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNotNull.call(builder,column)}if(predicateColumn===void 0){predicateColumn=column;predicateOperator="IS NOT NULL";predicateValue=void 0;predicateParameterized=!1;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"IS NOT NULL",value:void 0,parameterized:!1});return proxy},whereLike(column,value,caseSensitive=!1){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereLike.call(builder,column,value,caseSensitive)}if(!caseSensitive)return proxy.whereILike(column,value);if(predicateColumn===void 0){predicateColumn=column;predicateOperator="LIKE";predicateValue=value;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"LIKE",value,parameterized:!0});return proxy},whereNotLike(column,value,caseSensitive=!1){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNotLike.call(builder,column,value,caseSensitive)}if(!caseSensitive)return proxy.whereNotILike(column,value);if(predicateColumn===void 0){predicateColumn=column;predicateOperator="NOT LIKE";predicateValue=value;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:"NOT LIKE",value,parameterized:!0});return proxy},whereILike(column,value){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereILike.call(builder,column,value)}const sqlColumn=`LOWER(${column})`;if(predicateColumn===void 0){predicateColumn=sqlColumn;predicateOperator="LIKE LOWER";predicateValue=void 0;predicateParameterized=!1;predicateValues=[value]}else(additionalPredicates??=[]).push({column:sqlColumn,operator:"LIKE LOWER",value:void 0,parameterized:!1,values:[value]});return proxy},whereNotILike(column,value){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.whereNotILike.call(builder,column,value)}const sqlColumn=`LOWER(${column})`;if(predicateColumn===void 0){predicateColumn=sqlColumn;predicateOperator="NOT LIKE LOWER";predicateValue=void 0;predicateParameterized=!1;predicateValues=[value]}else(additionalPredicates??=[]).push({column:sqlColumn,operator:"NOT LIKE LOWER",value:void 0,parameterized:!1,values:[value]});return proxy},whereIn(column,values){if(typeof column!=="string"||!isSimpleSqliteColumn(column)||!Array.isArray(values)){const builder=materialize();return builder.whereIn.call(builder,column,values)}const snapshot=values.slice();if(predicateColumn===void 0){predicateColumn=column;predicateOperator="IN";predicateValue=void 0;predicateParameterized=!1;predicateValues=snapshot}else(additionalPredicates??=[]).push({column,operator:"IN",value:void 0,parameterized:!1,values:snapshot});return proxy},whereNotIn(column,values){if(typeof column!=="string"||!isSimpleSqliteColumn(column)||!Array.isArray(values)){const builder=materialize();return builder.whereNotIn.call(builder,column,values)}const snapshot=values.slice();if(predicateColumn===void 0){predicateColumn=column;predicateOperator="NOT IN";predicateValue=void 0;predicateParameterized=!1;predicateValues=snapshot}else(additionalPredicates??=[]).push({column,operator:"NOT IN",value:void 0,parameterized:!1,values:snapshot});return proxy},whereBetween(...args){const[column,startOrValues,end]=args,values=Array.isArray(startOrValues)?startOrValues:args.length>=3?[startOrValues,end]:void 0;if(typeof column!=="string"||!isSimpleSqliteColumn(column)||!values||values.length<2){const builder=materialize();return builder.whereBetween.call(builder,...args)}const lower=values[0],upper=values[1];if(predicateColumn===void 0){predicateColumn=column;predicateOperator=">=";predicateValue=lower;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator:">=",value:lower,parameterized:!0});(additionalPredicates??=[]).push({column,operator:"<=",value:upper,parameterized:!0});return proxy},whereDate(...args){const[column,operator,date]=args;if(typeof column!=="string"||!isSimpleSqliteColumn(column)||typeof operator!=="string"||!SIMPLE_SQLITE_OPERATORS.has(operator.toLowerCase())||typeof date!=="string"&&!(date instanceof Date)){const builder=materialize();return builder.whereDate.call(builder,...args)}const normalizedDate=date instanceof Date?date.toISOString():date;if(predicateColumn===void 0){predicateColumn=column;predicateOperator=operator;predicateValue=normalizedDate;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator,value:normalizedDate,parameterized:!0});return proxy},orderBy(column,direction="asc"){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.orderBy.call(builder,column,direction)}(orderings??=[]).push({column,direction:direction==="asc"?"asc":"desc"});return proxy},orderByDesc(column){if(typeof column!=="string"||!isSimpleSqliteColumn(column)){const builder=materialize();return builder.orderByDesc.call(builder,column)}(orderings??=[]).push({column,direction:"desc"});return proxy},latest(column){const resolvedColumn=column??queryBuilderConfig.timestamps.defaultOrderColumn;if(typeof resolvedColumn!=="string"||!isSimpleSqliteColumn(resolvedColumn)){const builder=materialize();return builder.latest.call(builder,column)}(orderings??=[]).push({column:resolvedColumn,direction:"desc"});return proxy},oldest(column){const resolvedColumn=column??queryBuilderConfig.timestamps.defaultOrderColumn;if(typeof resolvedColumn!=="string"||!isSimpleSqliteColumn(resolvedColumn)){const builder=materialize();return builder.oldest.call(builder,column)}(orderings??=[]).push({column:resolvedColumn,direction:"asc"});return proxy},offset(value){if(typeof value!=="number"||value<0||!Number.isInteger(value)){const builder=materialize();return builder.offset.call(builder,value)}rowOffset=value;return proxy}});let extensions;const base={select(value){if(materialized)return Reflect.apply(materialized.select,materialized,arguments);if(typeof value==="string"){if(value!=="*"&&!isSimpleSqliteSelection(value)){const builder=materialize();return builder.select.call(builder,value)}columns=value;selectedColumnsSql=value;return proxy}const selected=Array.isArray(value)?value:[value];let simple=!1,selection;const cached=lastSqliteSelection;if(cached&&cached.columns.length===selected.length){simple=!0;for(let index=0;simple&&index<selected.length;index++)simple=cached.columns[index]===selected[index];if(simple)selection=cached.sql}if(!simple){simple=selected.length>0;selection="";for(let index=0;simple&&index<selected.length;index++){const column=selected[index];simple=typeof column==="string"&&(column==="*"||isSimpleSqliteSelection(column));if(simple)selection+=index===0?column:`, ${column}`}if(simple)lastSqliteSelection={columns:selected.slice(),sql:selection}}if(!simple){const builder=materialize();return builder.select.call(builder,value)}columns=selected;selectedColumnsSql=selection;return proxy},where(column,operator,value){if(materialized)return Reflect.apply(materialized.where,materialized,arguments);if(typeof column!=="string"||!isSimpleSqliteColumn(column)||typeof operator!=="string"||operator!=="="&&!SIMPLE_SQLITE_OPERATORS.has(operator)&&!SIMPLE_SQLITE_OPERATORS.has(operator.toLowerCase())){if(typeof column==="string"&&isSimpleSqliteColumn(column)&&typeof operator==="string"){const normalized=operator.toLowerCase();if(normalized==="in"||normalized==="not in"){const values=Array.isArray(value)?snapshotSimpleSqliteMembershipValues(value):[value];if(values!==void 0){const membershipOperator=normalized==="in"?"IN":"NOT IN";if(predicateColumn===void 0){predicateColumn=column;predicateOperator=membershipOperator;predicateValue=void 0;predicateParameterized=!1;predicateValues=values}else(additionalPredicates??=[]).push({column,operator:membershipOperator,value:void 0,parameterized:!1,values});return proxy}}}return(extensions??=createExtensions()).where(column,operator,value)}if(predicateColumn===void 0){predicateColumn=column;predicateOperator=operator;predicateValue=value;predicateParameterized=!0;predicateValues=void 0}else(additionalPredicates??=[]).push({column,operator,value,parameterized:!0});return proxy},limit(value){if(materialized)return Reflect.apply(materialized.limit,materialized,arguments);if(typeof value!=="number"||value<0||!Number.isInteger(value)){const builder=materialize();return builder.limit.call(builder,value)}rowLimit=value;return proxy},execute(){if(materialized)return Reflect.apply(materialized.execute,materialized,arguments);try{return Promise.resolve(executeStatement())}catch(error){return Promise.reject(error)}},executeSync(){if(materialized)return Reflect.apply(materialized.executeSync,materialized,arguments);return executeStatement()},executeTakeFirstSync(){if(materialized)return Reflect.apply(materialized.executeTakeFirstSync,materialized,arguments);return executeFirstStatement()}};let forwardedMethods;proxy=new Proxy(base,{get(target,property){if(materialized){const builder=materialized,value=builder[property];return typeof value==="function"?value.bind(builder):value}if(property==="executeTakeFirst")return target.executeTakeFirst??=function(){if(materialized)return Reflect.apply(materialized.executeTakeFirst,materialized,arguments);try{return Promise.resolve(executeFirstStatement())}catch(error){return Promise.reject(error)}};if(GUARDED_SQLITE_SELECT_METHODS.has(property))return target[property];const cached=forwardedMethods?.[property];if(cached!==void 0)return cached;const value=target[property]??resolveDeferredSqliteTerminal(target,property,executeStatement,executeFirstStatement,materialize,proxy)??(extensions??=createExtensions())[property];if(value===void 0){const builder=materialize(),fallback=builder[property];return typeof fallback==="function"?fallback.bind(builder):fallback}if(typeof value!=="function")return value;const forward=(...args)=>{const builder=materialized;return builder?Reflect.apply(builder[property],builder,args):Reflect.apply(value,proxy,args)};(forwardedMethods??=Object.create(null))[property]=forward;return forward}});return proxy}function usesDeferredSqliteSelect(table){return getDialect()==="sqlite"&&!isLibsqlDriver(getDriver())&&!queryBuilderConfig.softDeletes?.enabled&&!hasActiveQueryBuilderHooks()&&isSimpleSqliteTable(table)}function selectFromDatabase(table){const instance=getDialect()==="sqlite"?getDb():getReadDb();if(usesDeferredSqliteSelect(table))return guardTransactionQuery(createDeferredSqliteSelect(instance,table));return guardTransactionQuery(instance.selectFrom(table))}function selectFromExplicitReadDatabase(table){const instance=getExplicitReadDb();if(usesDeferredSqliteSelect(table))return guardTransactionQuery(createDeferredSqliteSelect(instance,table));return guardTransactionQuery(instance.selectFrom(table))}function unsafeDatabase(query,params){return guardTransactionQuery(getDb().unsafe(query,params))}function unsafeExplicitReadDatabase(query,params){return guardTransactionQuery(getExplicitReadDb().unsafe(query,params))}function insertIntoDatabase(table){markContextWrote();return guardTransactionQuery(getDb().insertInto(table))}function updateTableDatabase(table){markContextWrote();return guardTransactionQuery(getDb().updateTable(table))}function deleteFromDatabase(table){markContextWrote();return guardTransactionQuery(getDb().deleteFrom(table))}function tableDatabase(table){return guardTransactionQuery(getDb().table(table))}function selectDatabase(table,...columns){return guardTransactionQuery(getReadDb().select(table,...columns))}function selectFromSubDatabase(subquery,alias){return guardTransactionQuery(getReadDb().selectFromSub(subquery,alias))}function tableExplicitReadDatabase(table){return guardTransactionQuery(getExplicitReadDb().table(table))}function selectExplicitReadDatabase(table,...columns){return guardTransactionQuery(getExplicitReadDb().select(table,...columns))}function selectFromSubExplicitReadDatabase(subquery,alias){return guardTransactionQuery(getExplicitReadDb().selectFromSub(subquery,alias))}function transactionalDatabase(callback,options){return(...args)=>getDb().transaction((tx)=>callback(tx,...args),options)}const dbFallback=new Proxy({},{get(_target,prop){const resolveInstance=typeof prop==="string"&&READ_ENTRY_POINTS.has(prop)?getReadDb:getDb,value=resolveInstance()[prop];if(typeof value==="function")return forwardDatabaseMethod(prop,resolveInstance);return value}});function forwardDatabaseMethod(prop,resolveInstance){const scope=transactionConnection.getStore();return(...args)=>{if(scope){assertTransactionConnection(scope);if(transactionConnection.getStore()!==scope)throw Error("A transaction query must execute in the callback that created it.")}const instance=resolveInstance();if(typeof prop==="string"&&WRITE_ENTRY_POINTS.has(prop))markContextWrote();return guardTransactionQuery(Reflect.apply(Reflect.get(instance,prop),instance,args))}}export const db=Object.create(dbFallback);Object.defineProperties(db,{deleteFrom:{value:deleteFromDatabase},fn:{value:aggregateFunctions},insertInto:{value:insertIntoDatabase},primary:{get:()=>primaryDb},read:{get:()=>readDb},select:{value:selectDatabase},selectFrom:{value:selectFromDatabase},selectFromSub:{value:selectFromSubDatabase},table:{value:tableDatabase},transactional:{value:transactionalDatabase},unsafe:{value:unsafeDatabase},updateTable:{value:updateTableDatabase}});const readDbFallback=new Proxy({},{get(_target,prop){const value=getExplicitReadDb()[prop];if(typeof value==="function")return forwardDatabaseMethod(prop,getExplicitReadDb);return value}});export const readDb=Object.create(readDbFallback);Object.defineProperties(readDb,{fn:{value:aggregateFunctions},select:{value:selectExplicitReadDatabase},selectFrom:{value:selectFromExplicitReadDatabase},selectFromSub:{value:selectFromSubExplicitReadDatabase},table:{value:tableExplicitReadDatabase},transactional:{value:transactionalDatabase},unsafe:{value:unsafeExplicitReadDatabase}});const primaryDb=new Proxy({},{get(_target,prop){const instance=getDb(),value=Reflect.get(instance,prop);return typeof value==="function"?forwardDatabaseMethod(prop,getDb):value}});export{setConfig};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/database",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.301",
|
|
6
6
|
"description": "The Stacks database integration.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -66,26 +66,26 @@
|
|
|
66
66
|
"prepublishOnly": "bun run build"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@stacksjs/config": "0.75.
|
|
70
|
-
"@stacksjs/env": "0.75.
|
|
71
|
-
"@stacksjs/error-handling": "0.75.
|
|
72
|
-
"@stacksjs/faker": "0.75.
|
|
73
|
-
"@stacksjs/features": "0.75.
|
|
74
|
-
"@stacksjs/logging": "0.75.
|
|
75
|
-
"@stacksjs/model-meta": "0.75.
|
|
76
|
-
"@stacksjs/path": "0.75.
|
|
77
|
-
"@stacksjs/query-builder": "0.75.
|
|
78
|
-
"@stacksjs/security": "0.75.
|
|
79
|
-
"@stacksjs/storage": "0.75.
|
|
80
|
-
"@stacksjs/strings": "0.75.
|
|
69
|
+
"@stacksjs/config": "0.75.301",
|
|
70
|
+
"@stacksjs/env": "0.75.301",
|
|
71
|
+
"@stacksjs/error-handling": "0.75.301",
|
|
72
|
+
"@stacksjs/faker": "0.75.301",
|
|
73
|
+
"@stacksjs/features": "0.75.301",
|
|
74
|
+
"@stacksjs/logging": "0.75.301",
|
|
75
|
+
"@stacksjs/model-meta": "0.75.301",
|
|
76
|
+
"@stacksjs/path": "0.75.301",
|
|
77
|
+
"@stacksjs/query-builder": "0.75.301",
|
|
78
|
+
"@stacksjs/security": "0.75.301",
|
|
79
|
+
"@stacksjs/storage": "0.75.301",
|
|
80
|
+
"@stacksjs/strings": "0.75.301",
|
|
81
81
|
"@stacksjs/ts-validation": "^0.5.6",
|
|
82
82
|
"bun-query-builder": "^0.3.9",
|
|
83
83
|
"dynamodb-tooling": "^0.3.6"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@stacksjs/cli": "0.75.
|
|
87
|
-
"@stacksjs/router": "0.75.
|
|
88
|
-
"@stacksjs/utils": "0.75.
|
|
86
|
+
"@stacksjs/cli": "0.75.301",
|
|
87
|
+
"@stacksjs/router": "0.75.301",
|
|
88
|
+
"@stacksjs/utils": "0.75.301",
|
|
89
89
|
"better-dx": "^0.2.24"
|
|
90
90
|
}
|
|
91
91
|
}
|