@stacksjs/database 0.74.31 → 0.74.33

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.
@@ -1,10 +1,11 @@
1
+ import type { DialectCapabilityOptions } from './dialect';
1
2
  /** Find capability violations in one file's SQL. */
2
- export declare function auditDdlSql(sql: string, file: string, dialect: string): DdlViolation[];
3
+ export declare function auditDdlSql(sql: string, file: string, dialect: string, options?: DialectCapabilityOptions): DdlViolation[];
3
4
  /** Audit a whole migration directory against the target dialect's capabilities. */
4
5
  export declare function auditDdlConstraints(options: {
5
6
  dir: string
6
7
  dialect: string
7
- }): DdlConstraintAudit;
8
+ } & DialectCapabilityOptions): DdlConstraintAudit;
8
9
  /**
9
10
  * The user-facing explanation.
10
11
  *
@@ -1,6 +1,6 @@
1
- import{existsSync,readdirSync,readFileSync}from"node:fs";import{join}from"node:path";import{dialectCapabilities}from"./dialect";import{stripSqlNoise}from"./migration-dialect";const CONSTRUCTS=[{capability:"foreignKeys",pattern:/\bFOREIGN\s+KEY\b/i,label:"FOREIGN KEY"},{capability:"foreignKeys",pattern:/\bREFERENCES\b/i,label:"REFERENCES"},{capability:"autoIncrement",pattern:/\bAUTO_INCREMENT\b(?!\s*=)/i,label:"AUTO_INCREMENT"},{capability:"createIndexIfNotExists",pattern:/\bCREATE\s+(?:UNIQUE\s+)?INDEX\s+IF\s+NOT\s+EXISTS\b/i,label:"CREATE INDEX IF NOT EXISTS"}];function supports(caps,capability){switch(capability){case"foreignKeys":return caps.supportsForeignKeys;case"autoIncrement":return caps.supportsAutoIncrement;case"createIndexIfNotExists":return caps.supportsCreateIndexIfNotExists}}export function auditDdlSql(sql,file,dialect){const caps=dialectCapabilities(dialect),lines=stripSqlNoise(sql).split(`
1
+ import{existsSync,readdirSync,readFileSync}from"node:fs";import{join}from"node:path";import{dialectCapabilities}from"./dialect";import{stripSqlNoise}from"./migration-dialect";const CONSTRUCTS=[{capability:"foreignKeys",pattern:/\bFOREIGN\s+KEY\b/i,label:"FOREIGN KEY"},{capability:"foreignKeys",pattern:/\bREFERENCES\b/i,label:"REFERENCES"},{capability:"autoIncrement",pattern:/\bAUTO_INCREMENT\b(?!\s*=)/i,label:"AUTO_INCREMENT"},{capability:"createIndexIfNotExists",pattern:/\bCREATE\s+(?:UNIQUE\s+)?INDEX\s+IF\s+NOT\s+EXISTS\b/i,label:"CREATE INDEX IF NOT EXISTS"}];function supports(caps,capability){switch(capability){case"foreignKeys":return caps.supportsForeignKeys;case"autoIncrement":return caps.supportsAutoIncrement;case"createIndexIfNotExists":return caps.supportsCreateIndexIfNotExists}}export function auditDdlSql(sql,file,dialect,options={}){const caps=dialectCapabilities(dialect,options),lines=stripSqlNoise(sql).split(`
2
2
  `),rawLines=sql.split(`
3
- `),found=[];for(let index=0;index<lines.length;index++)for(const{capability,pattern,label}of CONSTRUCTS){if(supports(caps,capability))continue;if(pattern.test(lines[index]??""))found.push({capability,construct:label,file,line:index+1,snippet:(rawLines[index]??"").trim().slice(0,120)})}return found}export function auditDdlConstraints(options){const{dir,dialect}=options;if(!existsSync(dir))return{total:0,violations:[],empty:!0};let files;try{files=readdirSync(dir).filter((f)=>f.endsWith(".sql")).sort()}catch{return{total:0,violations:[],empty:!0}}const violations=[];for(const file of files){let sql;try{sql=readFileSync(join(dir,file),"utf8")}catch{continue}violations.push(...auditDdlSql(sql,file,dialect))}return{total:files.length,violations,empty:files.length===0}}export const DDL_CONSTRAINT_OVERRIDE_ENV="STACKS_ALLOW_DDL_CONSTRAINT_VIOLATIONS";const REMEDIES={foreignKeys:["Distributed engines cannot enforce a foreign key across shards, so referential","integrity has to move into the application. Regenerate the corpus for this","dialect - the generator emits the backing index without the constraint - and","rely on the model relationships plus `buddy doctor` (which reports orphan rows)","instead of database-level cascades."].join(`
3
+ `),found=[];for(let index=0;index<lines.length;index++)for(const{capability,pattern,label}of CONSTRUCTS){if(supports(caps,capability))continue;if(pattern.test(lines[index]??""))found.push({capability,construct:label,file,line:index+1,snippet:(rawLines[index]??"").trim().slice(0,120)})}return found}export function auditDdlConstraints(options){const{dir,dialect,...capabilities}=options;if(!existsSync(dir))return{total:0,violations:[],empty:!0};let files;try{files=readdirSync(dir).filter((f)=>f.endsWith(".sql")).sort()}catch{return{total:0,violations:[],empty:!0}}const violations=[];for(const file of files){let sql;try{sql=readFileSync(join(dir,file),"utf8")}catch{continue}violations.push(...auditDdlSql(sql,file,dialect,capabilities))}return{total:files.length,violations,empty:files.length===0}}export const DDL_CONSTRAINT_OVERRIDE_ENV="STACKS_ALLOW_DDL_CONSTRAINT_VIOLATIONS";const REMEDIES={foreignKeys:["Distributed engines cannot enforce a foreign key across shards, so referential","integrity has to move into the application. Regenerate the corpus for this","dialect - the generator emits the backing index without the constraint - and","rely on the model relationships plus `buddy doctor` (which reports orphan rows)","instead of database-level cascades."].join(`
4
4
  `),autoIncrement:["Every shard would hand out the same AUTO_INCREMENT values and collide, so the","primary key has to come from somewhere else. Add `useUuid: true` to the model","traits for an application-generated key, or back the table with a sequence in","an unsharded keyspace and reference it from the VSchema."].join(`
5
5
  `),createIndexIfNotExists:["MySQL has no `CREATE INDEX IF NOT EXISTS` form and rejects it as a syntax","error. Regenerate the corpus for this dialect: the generator emits a bare","`CREATE INDEX` and treats the duplicate-key error on replay as success."].join(`
6
6
  `)};export function formatDdlConstraintError(audit,dialect,dir){const byCapability=new Map;for(const violation of audit.violations){const bucket=byCapability.get(violation.capability)??[];bucket.push(violation);byCapability.set(violation.capability,bucket)}const lines=[`The migration files in ${dir} use SQL features that ${dialect} does not implement.`,"","Nothing was migrated, so the database is unchanged.",""];for(const[capability,found]of byCapability){const files=new Set(found.map((v)=>v.file));lines.push(`${found.length} use(s) of ${found[0]?.construct} across ${files.size} file(s), for example:`);for(const violation of found.slice(0,3))lines.push(` ${violation.file}:${violation.line} ${violation.snippet}`);lines.push("");lines.push(REMEDIES[capability]);lines.push("")}lines.push(`If you know this corpus is correct, re-run with ${DDL_CONSTRAINT_OVERRIDE_ENV}=1 to proceed anyway.`);return lines.join(`
@@ -1 +1 @@
1
- import process from"node:process";import{SQL}from"bun";import{env as envVars}from"@stacksjs/env";import{DB_HOST_DEFAULT,DB_NAMES,DB_PORTS,DB_USERS,getConnectionDefaults}from"./defaults";const DEFAULT_TIMEOUT_MS=1e4,UNSAFE_IDENTIFIER_CHARS=/["'`\\;\u0000\n\r]/,MAX_IDENTIFIER_LENGTH=63;export function isValidDatabaseIdentifier(name){if(!name||name.length>MAX_IDENTIFIER_LENGTH)return!1;return!UNSAFE_IDENTIFIER_CHARS.test(name)}export function quoteIdentifier(dialect,name){return dialect==="mysql"?`\`${name}\``:`"${name}"`}function stripWrappingQuotes(value){return value.replace(/^['"]|['"]$/g,"")}export function classifyConnectionError(error){const e=error,errno=e?.errno,code=typeof e?.code==="string"?e.code:"",message=typeof e?.message==="string"?e.message:String(error??""),sqlState=typeof errno==="string"?errno.toUpperCase():"",mysqlErrno=typeof errno==="number"?errno:Number.NaN;if(sqlState==="3D000")return"missing-database";if(sqlState==="28000"||sqlState==="28P01")return sqlState==="28P01"?"auth-failed":"missing-role";if(sqlState==="42501")return"permission-denied";if(mysqlErrno===1049)return"missing-database";if(mysqlErrno===1045)return"auth-failed";if(mysqlErrno===1044)return"permission-denied";if(mysqlErrno===2002||mysqlErrno===2003)return"server-unreachable";if(code.includes("CONNECTION_REFUSED")||code.includes("CONNECTION_CLOSED")||code==="ECONNREFUSED"||code==="ENOTFOUND"||code==="EHOSTUNREACH")return"server-unreachable";if(code==="ETIMEDOUT"||code.includes("TIMEOUT"))return"timeout";if(/database .* does not exist|unknown database/i.test(message))return"missing-database";if(/role .* does not exist|user .* does not exist/i.test(message))return"missing-role";if(/password authentication failed|access denied for user/i.test(message))return"auth-failed";if(/permission denied|insufficient privilege/i.test(message))return"permission-denied";if(/econnrefused|connection refused|can'?t connect/i.test(message))return"server-unreachable";return"unknown"}export function resolveConnectionTarget(envProxy=envVars){const driver=String(envProxy.DB_CONNECTION||"sqlite");if(driver!=="postgres"&&driver!=="mysql"&&driver!=="singlestore")return null;const dialect=driver==="postgres"?"postgres":"mysql",defaults=getConnectionDefaults(dialect,envProxy),database=stripWrappingQuotes(String(envProxy.DB_DATABASE||defaults.database||DB_NAMES.default)),host=String(envProxy.DB_HOST||defaults.host||DB_HOST_DEFAULT),port=Number(envProxy.DB_PORT||defaults.port||DB_PORTS[dialect]),username=String(envProxy.DB_USERNAME||defaults.username||DB_USERS[dialect]),password=String(envProxy.DB_PASSWORD??defaults.password??"");return{dialect,driver,database,host,port,username,password,maintenanceCandidates:dialect==="postgres"?["postgres","template1"]:["information_schema","mysql"]}}export function buildConnectionUrl(target,database){const scheme=target.dialect==="postgres"?"postgres":"mysql",auth=target.password?`${encodeURIComponent(target.username)}:${encodeURIComponent(target.password)}`:encodeURIComponent(target.username),sslEnv=process.env.DB_SSL,ssl=sslEnv==="true"||sslEnv==="1"?"?ssl=true":"";return`${scheme}://${auth}@${target.host}:${target.port}/${encodeURIComponent(database)}${ssl}`}function defaultConnect(url){return new SQL(url)}async function withConnection(target,database,deps,work){const connect=deps.connect??defaultConnect,timeoutMs=deps.timeoutMs??Number(process.env.DB_PREFLIGHT_TIMEOUT_MS||DEFAULT_TIMEOUT_MS),client=connect(buildConnectionUrl(target,database));let timer;try{const timeout=new Promise((_,reject)=>{timer=setTimeout(()=>{const e=Error(`Timed out after ${timeoutMs}ms connecting to ${target.dialect} at ${target.host}:${target.port}`);e.code="ETIMEDOUT";reject(e)},timeoutMs)});return await Promise.race([work(client),timeout])}finally{if(timer)clearTimeout(timer);try{await client.close()}catch{}}}export async function probeTargetDatabase(target,deps={}){try{await withConnection(target,target.database,deps,async(client)=>client.unsafe("select 1"));return{ok:!0}}catch(error){return{ok:!1,kind:classifyConnectionError(error),error}}}export async function createDatabase(target,deps={}){if(!isValidDatabaseIdentifier(target.database))return{created:!1,kind:"unknown",error:Error(`Refusing to create a database named ${JSON.stringify(target.database)}. Database names must be 1 to 63 characters and may not contain quotes, backslashes, semicolons, or newlines.`)};const identifier=quoteIdentifier(target.dialect,target.database),sql=target.dialect==="mysql"?`CREATE DATABASE IF NOT EXISTS ${identifier}`:`CREATE DATABASE ${identifier}`;let lastError,lastKind="unknown";for(const candidate of target.maintenanceCandidates)try{await withConnection(target,candidate,deps,async(client)=>client.unsafe(sql));return{created:!0,via:candidate}}catch(error){const kind=classifyConnectionError(error),failure=error,message=failure?.message??"",errno=failure?.errno;if(errno==="42P04"||errno===1007||/already exists|database exists/i.test(String(message)))return{created:!1,via:candidate};lastError=error;lastKind=kind;if(kind==="permission-denied"||kind==="auth-failed"||kind==="missing-role")break}return{created:!1,kind:lastKind,error:lastError}}export async function canCreateDatabases(target,deps={}){if(target.dialect!=="postgres")return null;for(const candidate of target.maintenanceCandidates)try{return await withConnection(target,candidate,deps,async(client)=>{const rows=await client.unsafe("select rolcreatedb, rolsuper from pg_roles where rolname = current_user"),row=Array.isArray(rows)?rows[0]:void 0;if(!row)return null;return Boolean(row.rolcreatedb||row.rolsuper)})}catch{}return null}export function manualCreateHint(target){if(target.dialect==="postgres")return`createdb -h ${target.host} -p ${target.port} -U ${target.username} ${target.database}`;return`mysql -h ${target.host} -P ${target.port} -u ${target.username} -e "CREATE DATABASE \\\`${target.database}\\\`"`}export function describeTarget(target){return`the ${target.driver} connection (${target.host}:${target.port}, user "${target.username}")`}
1
+ import process from"node:process";import{SQL}from"bun";import{env as envVars}from"@stacksjs/env";import{DB_HOST_DEFAULT,DB_NAMES,DB_PORTS,DB_USERS,getConnectionDefaults}from"./defaults";const DEFAULT_TIMEOUT_MS=1e4,UNSAFE_IDENTIFIER_CHARS=/["'`\\;\0\n\r]/,MAX_IDENTIFIER_LENGTH=63;export function isValidDatabaseIdentifier(name){if(!name||name.length>MAX_IDENTIFIER_LENGTH)return!1;return!UNSAFE_IDENTIFIER_CHARS.test(name)}export function quoteIdentifier(dialect,name){return dialect==="mysql"?`\`${name}\``:`"${name}"`}function stripWrappingQuotes(value){return value.replace(/^['"]|['"]$/g,"")}export function classifyConnectionError(error){const e=error,errno=e?.errno,code=typeof e?.code==="string"?e.code:"",message=typeof e?.message==="string"?e.message:String(error??""),sqlState=typeof errno==="string"?errno.toUpperCase():"",mysqlErrno=typeof errno==="number"?errno:Number.NaN;if(sqlState==="3D000")return"missing-database";if(sqlState==="28000"||sqlState==="28P01")return sqlState==="28P01"?"auth-failed":"missing-role";if(sqlState==="42501")return"permission-denied";if(mysqlErrno===1049)return"missing-database";if(mysqlErrno===1045)return"auth-failed";if(mysqlErrno===1044)return"permission-denied";if(mysqlErrno===2002||mysqlErrno===2003)return"server-unreachable";if(code.includes("CONNECTION_REFUSED")||code.includes("CONNECTION_CLOSED")||code==="ECONNREFUSED"||code==="ENOTFOUND"||code==="EHOSTUNREACH")return"server-unreachable";if(code==="ETIMEDOUT"||code.includes("TIMEOUT"))return"timeout";if(/database .* does not exist|unknown database/i.test(message))return"missing-database";if(/role .* does not exist|user .* does not exist/i.test(message))return"missing-role";if(/password authentication failed|access denied for user/i.test(message))return"auth-failed";if(/permission denied|insufficient privilege/i.test(message))return"permission-denied";if(/econnrefused|connection refused|can'?t connect/i.test(message))return"server-unreachable";return"unknown"}export function resolveConnectionTarget(envProxy=envVars){const driver=String(envProxy.DB_CONNECTION||"sqlite");if(driver!=="postgres"&&driver!=="mysql"&&driver!=="singlestore")return null;const dialect=driver==="postgres"?"postgres":"mysql",defaults=getConnectionDefaults(dialect,envProxy),database=stripWrappingQuotes(String(envProxy.DB_DATABASE||defaults.database||DB_NAMES.default)),host=String(envProxy.DB_HOST||defaults.host||DB_HOST_DEFAULT),port=Number(envProxy.DB_PORT||defaults.port||DB_PORTS[dialect]),username=String(envProxy.DB_USERNAME||defaults.username||DB_USERS[dialect]),password=String(envProxy.DB_PASSWORD??defaults.password??"");return{dialect,driver,database,host,port,username,password,maintenanceCandidates:dialect==="postgres"?["postgres","template1"]:["information_schema","mysql"]}}export function buildConnectionUrl(target,database){const scheme=target.dialect==="postgres"?"postgres":"mysql",auth=target.password?`${encodeURIComponent(target.username)}:${encodeURIComponent(target.password)}`:encodeURIComponent(target.username),sslEnv=process.env.DB_SSL,ssl=sslEnv==="true"||sslEnv==="1"?"?ssl=true":"";return`${scheme}://${auth}@${target.host}:${target.port}/${encodeURIComponent(database)}${ssl}`}function defaultConnect(url){return new SQL(url)}async function withConnection(target,database,deps,work){const connect=deps.connect??defaultConnect,timeoutMs=deps.timeoutMs??Number(process.env.DB_PREFLIGHT_TIMEOUT_MS||DEFAULT_TIMEOUT_MS),client=connect(buildConnectionUrl(target,database));let timer;try{const timeout=new Promise((_,reject)=>{timer=setTimeout(()=>{const e=Error(`Timed out after ${timeoutMs}ms connecting to ${target.dialect} at ${target.host}:${target.port}`);e.code="ETIMEDOUT";reject(e)},timeoutMs)});return await Promise.race([work(client),timeout])}finally{if(timer)clearTimeout(timer);try{await client.close()}catch{}}}export async function probeTargetDatabase(target,deps={}){try{await withConnection(target,target.database,deps,async(client)=>client.unsafe("select 1"));return{ok:!0}}catch(error){return{ok:!1,kind:classifyConnectionError(error),error}}}export async function createDatabase(target,deps={}){if(!isValidDatabaseIdentifier(target.database))return{created:!1,kind:"unknown",error:Error(`Refusing to create a database named ${JSON.stringify(target.database)}. Database names must be 1 to 63 characters and may not contain quotes, backslashes, semicolons, or newlines.`)};const identifier=quoteIdentifier(target.dialect,target.database),sql=target.dialect==="mysql"?`CREATE DATABASE IF NOT EXISTS ${identifier}`:`CREATE DATABASE ${identifier}`;let lastError,lastKind="unknown";for(const candidate of target.maintenanceCandidates)try{await withConnection(target,candidate,deps,async(client)=>client.unsafe(sql));return{created:!0,via:candidate}}catch(error){const kind=classifyConnectionError(error),failure=error,message=failure?.message??"",errno=failure?.errno;if(errno==="42P04"||errno===1007||/already exists|database exists/i.test(String(message)))return{created:!1,via:candidate};lastError=error;lastKind=kind;if(kind==="permission-denied"||kind==="auth-failed"||kind==="missing-role")break}return{created:!1,kind:lastKind,error:lastError}}export async function canCreateDatabases(target,deps={}){if(target.dialect!=="postgres")return null;for(const candidate of target.maintenanceCandidates)try{return await withConnection(target,candidate,deps,async(client)=>{const rows=await client.unsafe("select rolcreatedb, rolsuper from pg_roles where rolname = current_user"),row=Array.isArray(rows)?rows[0]:void 0;if(!row)return null;return Boolean(row.rolcreatedb||row.rolsuper)})}catch{}return null}export function manualCreateHint(target){if(target.dialect==="postgres")return`createdb -h ${target.host} -p ${target.port} -U ${target.username} ${target.database}`;return`mysql -h ${target.host} -P ${target.port} -u ${target.username} -e "CREATE DATABASE \\\`${target.database}\\\`"`}export function describeTarget(target){return`the ${target.driver} connection (${target.host}:${target.port}, user "${target.username}")`}
@@ -167,17 +167,6 @@ export declare function statementTable(statement: string): string | null;
167
167
  * the conservative direction.
168
168
  */
169
169
  export declare function statementReferencesTable(statement: string, table: string): boolean;
170
- /**
171
- * Drop the statements that act on a table a disabled feature owns.
172
- *
173
- * The generator emits a catch-all `auto-misc` migration holding stray alters
174
- * for every table at once. Its filename names no table, so the file-level gate
175
- * cannot classify it, and it ran in full: the first statement touching a gated
176
- * table failed with "relation ... does not exist" and took the whole migration
177
- * run with it. Gating has to reach inside a file that mixes them.
178
- *
179
- * A statement whose table cannot be identified is kept.
180
- */
181
170
  export declare function withoutGatedStatements(sql: string, gated: ReadonlySet<string>): string;
182
171
  /**
183
172
  * Count how many migrations have been recorded as applied in the
@@ -10,7 +10,7 @@ ${missing.join(`
10
10
  migration TEXT NOT NULL UNIQUE,
11
11
  executed_at DATETIME DEFAULT CURRENT_TIMESTAMP
12
12
  )`);const insert=writeDb.prepare("INSERT OR IGNORE INTO migrations (migration) VALUES (?)");for(const migration of droppedMigrations)insert.run(migration);const unrecord=writeDb.prepare("DELETE FROM migrations WHERE migration = ?");for(const migration of replayMigrations)unrecord.run(migration)}finally{writeDb.close()}}catch(e){log.debug(`[migration] Could not record dropped migrations as executed: ${e}`)}return quarantined}function mayCreateMissingDatabase(){const signal=process.env.STACKS_CREATE_DATABASE;if(signal==="1")return!0;if(signal==="0")return!1;const policy=String(process.env.DB_CREATE_DATABASE||"").toLowerCase();return!(policy==="never"||policy==="false"||policy==="0")}function describeProbeFailure(target,kind,error){const where=describeTarget(target),detail=error instanceof Error?error.message:String(error??"");switch(kind){case"missing-role":return`The user "${target.username}" does not exist on ${where}. Set DB_USERNAME to a role that exists, or create it with: createuser -s ${target.username}`;case"auth-failed":return`Authentication failed for user "${target.username}" on ${where}. Check DB_USERNAME and DB_PASSWORD.`;case"server-unreachable":return`Could not reach the database server at ${target.host}:${target.port}. Check that it is running and that DB_HOST and DB_PORT are correct.`;case"timeout":return`Timed out connecting to the database server at ${target.host}:${target.port}. ${detail}`;case"permission-denied":return`The user "${target.username}" is not allowed to connect to "${target.database}" on ${where}.`;default:return`Could not connect to the database "${target.database}" on ${where}. ${detail}`}}async function ensureDatabaseExists(){const target=resolveConnectionTarget();if(!target)return;const probe=await probeTargetDatabase(target);if(probe.ok)return;if(probe.kind!=="missing-database")throw Error(describeProbeFailure(target,probe.kind,probe.error));if(!mayCreateMissingDatabase())throw Error(`The database "${target.database}" does not exist on ${describeTarget(target)}. Create it with: ${manualCreateHint(target)}`);const result=await createDatabase(target);if(!result.created&&result.error)throw Error(`The database "${target.database}" does not exist on ${describeTarget(target)} and it could not be created automatically. ${describeProbeFailure(target,result.kind,result.error)}
13
- Create it yourself with: ${manualCreateHint(target)}`);if(result.created)log.success(`Created database "${target.database}" on ${target.host}:${target.port}`)}let databaseBootstrapped=!1;export async function ensureDatabaseReady(){if(databaseBootstrapped)return;await ensureDatabaseExists();databaseBootstrapped=!0}export function resetDatabaseBootstrapCache(){databaseBootstrapped=!1}async function hideDisabledFeatureMigrations(){const hidden=[];try{const{appModelClaimsTable,FEATURE_NAMES,migrationFeature,migrationTable}=await import("@stacksjs/features"),{feature:isFeatureEnabled}=await import("@stacksjs/config"),fs=await import("node:fs/promises"),migrationsDir=migrationDirectory();if(!existsSync(migrationsDir))return hidden;const disabledFeatures=new Set(FEATURE_NAMES.filter((f)=>!isFeatureEnabled(f)));if(disabledFeatures.size===0)return hidden;const files=readdirSync(migrationsDir).filter((f)=>f.endsWith(".sql")),gatedTables=new Set;for(const file of files){const owner=migrationFeature(file);if(!owner||!disabledFeatures.has(owner))continue;const table=migrationTable(file);if(table&&appModelClaimsTable(table))continue;if(table)gatedTables.add(table.toLowerCase());const original=join(migrationsDir,file),hiddenPath=`${original}.disabled`;await fs.rename(original,hiddenPath);hidden.push({original,hidden:hiddenPath,feature:owner})}for(const file of files){const filePath=join(migrationsDir,file);if(!existsSync(filePath))continue;const sql=readFileSync(filePath,"utf8"),filtered=withoutGatedStatements(sql,gatedTables);if(filtered===sql)continue;const backup=`${filePath}.ungated`;await fs.rename(filePath,backup);writeFileSync(filePath,filtered);hidden.push({original:filePath,hidden:backup,feature:"mixed"})}if(hidden.length>0){const summary=Object.entries(hidden.reduce((acc,h)=>{acc[h.feature]=(acc[h.feature]??0)+1;return acc},{})).map(([f,n])=>`${f}: ${n}`).join(", ");log.info(`[migration] Skipping ${hidden.length} migration(s) for disabled features (${summary}). Run \`./buddy <feature>:install\` to enable.`)}}catch{}return hidden}function afterCommonTableExpressions(statement){if(!/^\s*WITH\b/i.test(statement))return statement;let depth=0;for(let index=0;index<statement.length;index++){const char=statement[index];if(char==="("){depth++;continue}if(char!==")")continue;depth--;if(depth!==0)continue;const rest=statement.slice(index+1);if(/^\s*,/.test(rest))continue;return rest}return statement}export function statementTable(statement){const body=afterCommonTableExpressions(statement),patterns=[/^\s*ALTER\s+TABLE\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?([a-z0-9_]+)["`]?/i,/^\s*DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?["`]?([a-z0-9_]+)["`]?/i,/^\s*CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?[a-z0-9_]+["`]?\s+ON\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*UPDATE\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*INSERT\s+(?:OR\s+\w+\s+)?INTO\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*DELETE\s+FROM\s+["`]?([a-z0-9_]+)["`]?/i];for(const pattern of patterns){const match=pattern.exec(body);if(match)return match[1].toLowerCase()}return null}export function statementReferencesTable(statement,table){const escaped=table.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`\\b(?:FROM|JOIN|UPDATE|INTO)\\s+["\`\\[]?${escaped}["\`\\]]?\\b`,"i").test(statement)}export function withoutGatedStatements(sql,gated){if(gated.size===0)return sql;const statements=sql.split(";").map((s)=>s.trim()).filter(Boolean),kept=statements.filter((statement)=>{const table=statementTable(statement);if(table&&gated.has(table))return!1;for(const candidate of gated)if(statementReferencesTable(statement,candidate))return!1;return!0});if(kept.length===statements.length)return sql;return kept.length===0?"":`${kept.join(`;
13
+ Create it yourself with: ${manualCreateHint(target)}`);if(result.created)log.success(`Created database "${target.database}" on ${target.host}:${target.port}`)}let databaseBootstrapped=!1;export async function ensureDatabaseReady(){if(databaseBootstrapped)return;await ensureDatabaseExists();databaseBootstrapped=!0}export function resetDatabaseBootstrapCache(){databaseBootstrapped=!1}async function hideDisabledFeatureMigrations(){const hidden=[];try{const{appModelClaimsTable,FEATURE_NAMES,migrationFeature,migrationTable}=await import("@stacksjs/features"),{feature:isFeatureEnabled}=await import("@stacksjs/config"),fs=await import("node:fs/promises"),migrationsDir=migrationDirectory();if(!existsSync(migrationsDir))return hidden;const disabledFeatures=new Set(FEATURE_NAMES.filter((f)=>!isFeatureEnabled(f)));if(disabledFeatures.size===0)return hidden;const files=readdirSync(migrationsDir).filter((f)=>f.endsWith(".sql")),gatedTables=new Set;for(const file of files){const owner=migrationFeature(file);if(!owner||!disabledFeatures.has(owner))continue;const table=migrationTable(file);if(table&&appModelClaimsTable(table))continue;if(table)gatedTables.add(table.toLowerCase());const original=join(migrationsDir,file),hiddenPath=`${original}.disabled`;await fs.rename(original,hiddenPath);hidden.push({original,hidden:hiddenPath,feature:owner})}for(const file of files){const filePath=join(migrationsDir,file);if(!existsSync(filePath))continue;const sql=readFileSync(filePath,"utf8"),filtered=withoutGatedStatements(sql,gatedTables);if(filtered===sql)continue;const backup=`${filePath}.ungated`;await fs.rename(filePath,backup);writeFileSync(filePath,filtered);hidden.push({original:filePath,hidden:backup,feature:"mixed"})}if(hidden.length>0){const summary=Object.entries(hidden.reduce((acc,h)=>{acc[h.feature]=(acc[h.feature]??0)+1;return acc},{})).map(([f,n])=>`${f}: ${n}`).join(", ");log.info(`[migration] Skipping ${hidden.length} migration(s) for disabled features (${summary}). Run \`./buddy <feature>:install\` to enable.`)}}catch{}return hidden}function afterCommonTableExpressions(statement){if(!/^\s*WITH\b/i.test(statement))return statement;let depth=0;for(let index=0;index<statement.length;index++){const char=statement[index];if(char==="("){depth++;continue}if(char!==")")continue;depth--;if(depth!==0)continue;const rest=statement.slice(index+1);if(/^\s*,/.test(rest))continue;return rest}return statement}export function statementTable(statement){const body=afterCommonTableExpressions(statement),patterns=[/^\s*ALTER\s+TABLE\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?([a-z0-9_]+)["`]?/i,/^\s*DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?["`]?([a-z0-9_]+)["`]?/i,/^\s*CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`]?[a-z0-9_]+["`]?\s+ON\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*UPDATE\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*INSERT\s+(?:OR\s+\w+\s+)?INTO\s+["`]?([a-z0-9_]+)["`]?/i,/^\s*DELETE\s+FROM\s+["`]?([a-z0-9_]+)["`]?/i];for(const pattern of patterns){const match=pattern.exec(body);if(match)return match[1].toLowerCase()}return null}export function statementReferencesTable(statement,table){const escaped=table.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`\\b(?:FROM|JOIN|UPDATE|INTO)\\s+["\`\\[]?${escaped}["\`\\]]?\\b`,"i").test(statement)}function rebuiltTable(statement){const rename=/^\s*ALTER\s+TABLE\s+["`]?([a-z0-9_]+)["`]?\s+RENAME\s+TO\s+["`]?([a-z0-9_]+)["`]?/i.exec(statement);if(rename?.[2])return rename[2].toLowerCase();const table=statementTable(statement);if(table?.startsWith("_qb_tmp_"))return table.slice(8);return null}export function withoutGatedStatements(sql,gated){if(gated.size===0)return sql;const statements=sql.split(";").map((s)=>s.trim()).filter(Boolean),kept=statements.filter((statement)=>{const table=rebuiltTable(statement)??statementTable(statement);if(table&&gated.has(table))return!1;for(const candidate of gated)if(statementReferencesTable(statement,candidate))return!1;return!0});if(kept.length===statements.length)return sql;return kept.length===0?"":`${kept.join(`;
14
14
  `)};
15
15
  `}async function restoreHiddenMigrations(hidden){const fs=await import("node:fs/promises");for(const{original,hidden:h}of hidden)try{if(h.endsWith(".ungated"))await fs.rm(original,{force:!0});await fs.rename(h,original)}catch{}}export async function countAppliedMigrations(){try{const row=await db.selectFrom("migrations").select((eb)=>eb.fn.count("id").as("n")).executeTakeFirst();if(!row)return 0;const n=Number(row.n??row.N??0);return Number.isFinite(n)?n:0}catch{return 0}}async function writeMigrateMarker(appliedCount){try{const fs=await import("node:fs/promises"),dir=path.frameworkRuntimePath();await fs.mkdir(dir,{recursive:!0});const file=`${dir}/last-migrate-result.json`,body=JSON.stringify({appliedCount,completedAt:new Date().toISOString()});await fs.writeFile(file,body,"utf8")}catch{}}export function idempotentSql(sql){const header=/^(?:[^\S\n]*--[^\n]*\n)+/.exec(sql)?.[0]??"",stmts=sql.slice(header.length).split(";").map((s)=>s.trim()).filter(Boolean);if(stmts.length===0)return sql;const out=[];for(const raw of stmts){const stmt=raw.replace(/\bADD\s+COLUMN\s+(?!IF\s+NOT\s+EXISTS\b)/gi,"ADD COLUMN IF NOT EXISTS "),m=/^ALTER\s+TABLE\s+("?\w+"?)\s+ADD\s+CONSTRAINT\s+("?\w+"?)/i.exec(stmt);if(m){const drops=[`ALTER TABLE ${m[1]} DROP CONSTRAINT IF EXISTS ${m[2]}`],fk=/\bFOREIGN\s+KEY\s*\(\s*"?(\w+)"?\s*\)/i.exec(stmt);if(fk){const table=m[1].replace(/"/g,"");drops.push(`ALTER TABLE ${m[1]} DROP CONSTRAINT IF EXISTS "${table}_${fk[1]}_fkey"`)}const already=new Set;for(let i=out.length-1;i>=0;i--){const previous=out[i];if(!/^ALTER\s+TABLE\s+"?\w+"?\s+DROP\s+CONSTRAINT\b/i.test(previous))break;already.add(previous.toUpperCase())}for(const drop of drops)if(!already.has(drop.toUpperCase()))out.push(drop)}out.push(stmt)}return`${header}${out.join(`;
16
16
  `)};
@@ -1,3 +1,4 @@
1
+ import type { DialectCapabilityOptions } from './dialect';
1
2
  /**
2
3
  * The framework's canonical datetime literal: ISO-8601 UTC **without** the
3
4
  * trailing `Z` (`2026-08-04T01:52:47.417`).
@@ -67,7 +68,7 @@ export declare function parseSqlDateTime(value: unknown): Date | null;
67
68
  * await db.unsafe(`SELECT * FROM users WHERE id = ${sql.param(1)}`, [userId])
68
69
  * ```
69
70
  */
70
- export declare function sqlHelpers(driver: string): SqlDialectHelpers;
71
+ export declare function sqlHelpers(driver: string, options?: DialectCapabilityOptions): SqlDialectHelpers;
71
72
  export declare interface SqlDialectHelpers {
72
73
  driver: string
73
74
  isPostgres: boolean
@@ -1 +1 @@
1
- import{dialectCapabilities}from"./dialect";let lastSqlTimestampMs,lastSqlTimestamp="";export function sqlDateTime(value){if(value!==void 0)return value.toISOString().slice(0,-1);const now=Date.now();if(now!==lastSqlTimestampMs){lastSqlTimestamp=new Date(now).toISOString().slice(0,-1);lastSqlTimestampMs=now}return lastSqlTimestamp}export function sqlDateTimeLiteral(value){return`'${sqlDateTime(value)}'`}export function parseSqlDateTime(value){if(value===null||value===void 0)return null;if(value instanceof Date)return Number.isNaN(value.getTime())?null:value;if(typeof value==="number")return Number.isNaN(value)?null:new Date(value);if(typeof value!=="string")return null;const trimmed=value.trim();if(!trimmed)return null;let normalized=trimmed.replace(" ","T");if(!/(?:Z|[+-]\d{2}:?\d{2})$/i.test(normalized))normalized+="Z";const parsed=new Date(normalized);return Number.isNaN(parsed.getTime())?null:parsed}export function sqlHelpers(driver){const caps=dialectCapabilities(driver),isPostgres=caps.wire==="postgres",isMysql=caps.wire==="mysql",isSqlite=caps.wire==="sqlite";return{driver,isPostgres,isMysql,isSqlite,now:isPostgres||isMysql?"NOW()":"datetime('now')",utcNow:isPostgres?"(now() AT TIME ZONE 'utc')":isMysql?"(UTC_TIMESTAMP)":"CURRENT_TIMESTAMP",boolTrue:isPostgres?"true":"1",boolFalse:isPostgres?"false":"0",autoIncrement:isPostgres?"SERIAL":"INTEGER",bigInteger:isSqlite?"INTEGER":"BIGINT",primaryKey:!caps.supportsAutoIncrement?"PRIMARY KEY":isPostgres?"PRIMARY KEY":isMysql?"PRIMARY KEY AUTO_INCREMENT":"PRIMARY KEY AUTOINCREMENT",pkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id SERIAL PRIMARY KEY":isMysql?"id INTEGER PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",bigPkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id BIGSERIAL PRIMARY KEY":isMysql?"id BIGINT PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",datetime:isMysql?"DATETIME":"TIMESTAMP",nullableTimestamp:isMysql?"DATETIME NULL":"TIMESTAMP",param(index){return isPostgres?`$${index}`:"?"},params(...values){if(isPostgres)return{sql:values.map((_,i)=>`$${i+1}`).join(", "),values};return{sql:values.map(()=>"?").join(", "),values}}}}
1
+ import{dialectCapabilities}from"./dialect";let lastSqlTimestampMs,lastSqlTimestamp="";export function sqlDateTime(value){if(value!==void 0)return value.toISOString().slice(0,-1);const now=Date.now();if(now!==lastSqlTimestampMs){lastSqlTimestamp=new Date(now).toISOString().slice(0,-1);lastSqlTimestampMs=now}return lastSqlTimestamp}export function sqlDateTimeLiteral(value){return`'${sqlDateTime(value)}'`}export function parseSqlDateTime(value){if(value===null||value===void 0)return null;if(value instanceof Date)return Number.isNaN(value.getTime())?null:value;if(typeof value==="number")return Number.isNaN(value)?null:new Date(value);if(typeof value!=="string")return null;const trimmed=value.trim();if(!trimmed)return null;let normalized=trimmed.replace(" ","T");if(!/(?:Z|[+-]\d{2}:?\d{2})$/i.test(normalized))normalized+="Z";const parsed=new Date(normalized);return Number.isNaN(parsed.getTime())?null:parsed}export function sqlHelpers(driver,options={}){const caps=dialectCapabilities(driver,options),isPostgres=caps.wire==="postgres",isMysql=caps.wire==="mysql",isSqlite=caps.wire==="sqlite";return{driver,isPostgres,isMysql,isSqlite,now:isPostgres||isMysql?"NOW()":"datetime('now')",utcNow:isPostgres?"(now() AT TIME ZONE 'utc')":isMysql?"(UTC_TIMESTAMP)":"CURRENT_TIMESTAMP",boolTrue:isPostgres?"true":"1",boolFalse:isPostgres?"false":"0",autoIncrement:isPostgres?"SERIAL":"INTEGER",bigInteger:isSqlite?"INTEGER":"BIGINT",primaryKey:!caps.supportsAutoIncrement?"PRIMARY KEY":isPostgres?"PRIMARY KEY":isMysql?"PRIMARY KEY AUTO_INCREMENT":"PRIMARY KEY AUTOINCREMENT",pkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id SERIAL PRIMARY KEY":isMysql?"id INTEGER PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",bigPkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id BIGSERIAL PRIMARY KEY":isMysql?"id BIGINT PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",datetime:isMysql?"DATETIME":"TIMESTAMP",nullableTimestamp:isMysql?"DATETIME NULL":"TIMESTAMP",param(index){return isPostgres?`$${index}`:"?"},params(...values){if(isPostgres)return{sql:values.map((_,i)=>`$${i+1}`).join(", "),values};return{sql:values.map(()=>"?").join(", "),values}}}}
package/dist/utils.js CHANGED
@@ -1 +1 @@
1
- import{AsyncLocalStorage}from"node:async_hooks";import{config as queryBuilderConfig,createQueryBuilder,registerPersistentQueryHooks,resetConnection as resetQueryBuilderConnection,setConfig}from"@stacksjs/query-builder";import{SQL}from"bun";import{env as envVars}from"@stacksjs/env";import{getConnectionDefaults}from"./defaults";import{isMysqlWire,isVitessSharded,toQueryBuilderDialect}from"./dialect";import{relativeMigrationDirectory,resolveMigrationDirectory,snapshotDirForQueryBuilder}from"./migration-path";import{configureDatabaseRoutingContext,contextInTransaction,markContextWrote,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=envVars.DB_CONNECTION||"sqlite",queryBuilderDialect=toQueryBuilderDialect(dbDriver),queryLoggingEnabled=envVars.DB_QUERY_LOGGING_ENABLED??!isProductionEnvironment(appEnv),dbConfig={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:""}}},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=config.database.default;queryBuilderDialect=toQueryBuilderDialect(dbDriver);if(config?.database?.connections)dbConfig=config.database;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}function getDialect(){return queryBuilderDialect}function getDbConfig(){const driver=getDriver(),database=getDatabaseConfig(),env=getEnv();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){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")return;return getDatabaseConfig().connections?.[driver]?.pool}const EMPTY_REPLICAS=[];function getReplicas(){const driver=getDriver();if(driver==="sqlite")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);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");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 serializeSqliteTransaction(run){if(sqliteTxOwner.getStore())return run();const result=sqliteTxTail.then(()=>sqliteTxOwner.run(!0,run));sqliteTxTail=result.then(()=>{return},()=>{return});return result}function applySqliteTransactionSerialization(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>serializeSqliteTransaction(()=>original(...args))}function applyTransactionRoutingContext(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>withTransactionContext(()=>original(...args))}function getDb(){if(!_dbInstance){updateQueryBuilderConfig();_dbInstance=createQueryBuilder();if(getDialect()==="sqlite")applySqliteTransactionSerialization(_dbInstance);applyTransactionRoutingContext(_dbInstance)}return _dbInstance}let _replicaInstances=new Map;export function resetDatabaseConnection(){resetQueryBuilderConnection();_dbInstance=null;_replicaInstances=new Map}function getReplicaDb(replica){const primary=getDbConfig(),resolved=resolveReplicaConnection(replica,primary),key=`${resolved.host}:${resolved.port??""}`,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())}),instance=createQueryBuilder({sql});_replicaInstances.set(key,instance);return instance}function getReadDb(){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 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;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"||property==="executeTakeFirst"){const first=async()=>executeFirstStatement();target.first=first;target.executeTakeFirst=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;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){const statement=sqliteDatabase.query(sql);return params?statement.all(...params):statement.all()}return instance.unsafe(sql,params).executeSync()}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+=` (${predicateValues.map(()=>"?").join(", ")})`;params.push(...predicateValues)}else if(predicateParameterized){query+=" ?";params.push(predicateValue)}if(additionalPredicates)query+=` AND ${additionalPredicates.map((predicate)=>{if(predicate.values){params.push(...predicate.values);return`${predicate.column} ${predicate.operator} (${predicate.values.map(()=>"?").join(", ")})`}if(predicate.parameterized){params.push(predicate.value);return`${predicate.column} ${predicate.operator} ?`}return`${predicate.column} ${predicate.operator}`}).join(" AND ")}`}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;proxy=new Proxy({select(value){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(typeof column!=="string"||!isSimpleSqliteColumn(column)||typeof operator!=="string"||operator!=="="&&!SIMPLE_SQLITE_OPERATORS.has(operator)&&!SIMPLE_SQLITE_OPERATORS.has(operator.toLowerCase()))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(typeof value!=="number"||value<0||!Number.isInteger(value)){const builder=materialize();return builder.limit.call(builder,value)}rowLimit=value;return proxy},async execute(){return executeStatement()},executeSync(){return executeStatement()},executeTakeFirstSync(){return executeFirstStatement()}},{get(target,property){const value=target[property];if(value!==void 0)return value;const terminal=resolveDeferredSqliteTerminal(target,property,executeStatement,executeFirstStatement,materialize,proxy);if(terminal!==void 0)return terminal;const extension=(extensions??=createExtensions())[property];if(extension!==void 0)return extension;const builder=materialize(),fallback=builder[property];return typeof fallback==="function"?fallback.bind(builder):fallback}});return proxy}function selectFromDatabase(table){const dialect=getDialect(),instance=dialect==="sqlite"?getDb():getReadDb();if(dialect==="sqlite"&&!queryBuilderConfig.softDeletes?.enabled&&!hasActiveQueryBuilderHooks()&&isSimpleSqliteTable(table))return createDeferredSqliteSelect(instance,table);return instance.selectFrom(table)}function selectFromExplicitReadDatabase(table){const dialect=getDialect(),instance=getExplicitReadDb();if(dialect==="sqlite"&&!queryBuilderConfig.softDeletes?.enabled&&!hasActiveQueryBuilderHooks()&&isSimpleSqliteTable(table))return createDeferredSqliteSelect(instance,table);return instance.selectFrom(table)}function unsafeDatabase(query,params){return getDb().unsafe(query,params)}function unsafeExplicitReadDatabase(query,params){return getExplicitReadDb().unsafe(query,params)}function insertIntoDatabase(table){markContextWrote();return getDb().insertInto(table)}function updateTableDatabase(table){markContextWrote();return getDb().updateTable(table)}function deleteFromDatabase(table){markContextWrote();return getDb().deleteFrom(table)}function tableDatabase(table){return getDb().table(table)}function selectDatabase(table,...columns){return getReadDb().select(table,...columns)}function selectFromSubDatabase(subquery,alias){return getReadDb().selectFromSub(subquery,alias)}function tableExplicitReadDatabase(table){return getExplicitReadDb().table(table)}function selectExplicitReadDatabase(table,...columns){return getExplicitReadDb().select(table,...columns)}function selectFromSubExplicitReadDatabase(subquery,alias){return getExplicitReadDb().selectFromSub(subquery,alias)}const dbFallback=new Proxy({},{get(_target,prop){if(typeof prop==="string"&&WRITE_ENTRY_POINTS.has(prop))markContextWrote();const instance=typeof prop==="string"&&READ_ENTRY_POINTS.has(prop)?getReadDb():getDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);return value}});export const db=Object.create(dbFallback);Object.defineProperties(db,{deleteFrom:{value:deleteFromDatabase},fn:{value:aggregateFunctions},insertInto:{value:insertIntoDatabase},read:{get:()=>readDb},select:{value:selectDatabase},selectFrom:{value:selectFromDatabase},selectFromSub:{value:selectFromSubDatabase},table:{value:tableDatabase},unsafe:{value:unsafeDatabase},updateTable:{value:updateTableDatabase}});const readDbFallback=new Proxy({},{get(_target,prop){const instance=getExplicitReadDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);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},unsafe:{value:unsafeExplicitReadDatabase}});export{setConfig};
1
+ import{AsyncLocalStorage}from"node:async_hooks";import{types as nodeUtilTypes}from"node:util";import{config as queryBuilderConfig,createQueryBuilder,registerPersistentQueryHooks,resetConnection as resetQueryBuilderConnection,setConfig}from"@stacksjs/query-builder";import{SQL}from"bun";import{runInTransactionScope}from"./transaction-context";import{env as envVars}from"@stacksjs/env";import{getConnectionDefaults}from"./defaults";import{isMysqlWire,isVitessSharded,toQueryBuilderDialect}from"./dialect";import{relativeMigrationDirectory,resolveMigrationDirectory,snapshotDirForQueryBuilder}from"./migration-path";import{configureDatabaseRoutingContext,contextInTransaction,markContextWrote,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=envVars.DB_CONNECTION||"sqlite",queryBuilderDialect=toQueryBuilderDialect(dbDriver),queryLoggingEnabled=envVars.DB_QUERY_LOGGING_ENABLED??!isProductionEnvironment(appEnv),dbConfig={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:""}}},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=config.database.default;queryBuilderDialect=toQueryBuilderDialect(dbDriver);if(config?.database?.connections)dbConfig=config.database;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}function getDialect(){return queryBuilderDialect}function getDbConfig(){const driver=getDriver(),database=getDatabaseConfig(),env=getEnv();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){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")return;return getDatabaseConfig().connections?.[driver]?.pool}const EMPTY_REPLICAS=[];function getReplicas(){const driver=getDriver();if(driver==="sqlite")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);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");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 serializeSqliteTransaction(run){if(sqliteTxOwner.getStore())return run();const result=sqliteTxTail.then(()=>sqliteTxOwner.run(!0,run));sqliteTxTail=result.then(()=>{return},()=>{return});return result}function applySqliteTransactionSerialization(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>serializeSqliteTransaction(()=>original(...args))}function applyTransactionDispatchScope(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>runInTransactionScope(()=>original(...args))}function applyTransactionRoutingContext(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>withTransactionContext(()=>original(...args))}function getDb(){if(!_dbInstance){updateQueryBuilderConfig();_dbInstance=createQueryBuilder();if(getDialect()==="sqlite")applySqliteTransactionSerialization(_dbInstance);applyTransactionDispatchScope(_dbInstance);applyTransactionRoutingContext(_dbInstance)}return _dbInstance}let _replicaInstances=new Map;export function resetDatabaseConnection(){resetQueryBuilderConnection();lastGeneralSqliteStatement=void 0;_dbInstance=null;_replicaInstances=new Map}function getReplicaDb(replica){const primary=getDbConfig(),resolved=resolveReplicaConnection(replica,primary),key=`${resolved.host}:${resolved.port??""}`,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())}),instance=createQueryBuilder({sql});_replicaInstances.set(key,instance);return instance}function getReadDb(){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 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(nodeUtilTypes.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 selectFromDatabase(table){const dialect=getDialect(),instance=dialect==="sqlite"?getDb():getReadDb();if(dialect==="sqlite"&&!queryBuilderConfig.softDeletes?.enabled&&!hasActiveQueryBuilderHooks()&&isSimpleSqliteTable(table))return createDeferredSqliteSelect(instance,table);return instance.selectFrom(table)}function selectFromExplicitReadDatabase(table){const dialect=getDialect(),instance=getExplicitReadDb();if(dialect==="sqlite"&&!queryBuilderConfig.softDeletes?.enabled&&!hasActiveQueryBuilderHooks()&&isSimpleSqliteTable(table))return createDeferredSqliteSelect(instance,table);return instance.selectFrom(table)}function unsafeDatabase(query,params){return getDb().unsafe(query,params)}function unsafeExplicitReadDatabase(query,params){return getExplicitReadDb().unsafe(query,params)}function insertIntoDatabase(table){markContextWrote();return getDb().insertInto(table)}function updateTableDatabase(table){markContextWrote();return getDb().updateTable(table)}function deleteFromDatabase(table){markContextWrote();return getDb().deleteFrom(table)}function tableDatabase(table){return getDb().table(table)}function selectDatabase(table,...columns){return getReadDb().select(table,...columns)}function selectFromSubDatabase(subquery,alias){return getReadDb().selectFromSub(subquery,alias)}function tableExplicitReadDatabase(table){return getExplicitReadDb().table(table)}function selectExplicitReadDatabase(table,...columns){return getExplicitReadDb().select(table,...columns)}function selectFromSubExplicitReadDatabase(subquery,alias){return getExplicitReadDb().selectFromSub(subquery,alias)}const dbFallback=new Proxy({},{get(_target,prop){if(typeof prop==="string"&&WRITE_ENTRY_POINTS.has(prop))markContextWrote();const instance=typeof prop==="string"&&READ_ENTRY_POINTS.has(prop)?getReadDb():getDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);return value}});export const db=Object.create(dbFallback);Object.defineProperties(db,{deleteFrom:{value:deleteFromDatabase},fn:{value:aggregateFunctions},insertInto:{value:insertIntoDatabase},read:{get:()=>readDb},select:{value:selectDatabase},selectFrom:{value:selectFromDatabase},selectFromSub:{value:selectFromSubDatabase},table:{value:tableDatabase},unsafe:{value:unsafeDatabase},updateTable:{value:updateTableDatabase}});const readDbFallback=new Proxy({},{get(_target,prop){const instance=getExplicitReadDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);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},unsafe:{value:unsafeExplicitReadDatabase}});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.74.31",
5
+ "version": "0.74.33",
6
6
  "description": "The Stacks database integration.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -60,26 +60,26 @@
60
60
  "prepublishOnly": "bun run build"
61
61
  },
62
62
  "dependencies": {
63
- "@stacksjs/config": "0.74.31",
64
- "@stacksjs/env": "0.74.31",
65
- "@stacksjs/error-handling": "0.74.31",
66
- "@stacksjs/faker": "^0.74.31",
67
- "@stacksjs/features": "0.74.31",
68
- "@stacksjs/logging": "0.74.31",
69
- "@stacksjs/model-meta": "0.74.31",
70
- "@stacksjs/path": "0.74.31",
71
- "@stacksjs/query-builder": "^0.74.31",
72
- "@stacksjs/security": "0.74.31",
73
- "@stacksjs/storage": "0.74.31",
74
- "@stacksjs/strings": "0.74.31",
63
+ "@stacksjs/config": "0.74.33",
64
+ "@stacksjs/env": "0.74.33",
65
+ "@stacksjs/error-handling": "0.74.33",
66
+ "@stacksjs/faker": "^0.74.33",
67
+ "@stacksjs/features": "0.74.33",
68
+ "@stacksjs/logging": "0.74.33",
69
+ "@stacksjs/model-meta": "0.74.33",
70
+ "@stacksjs/path": "0.74.33",
71
+ "@stacksjs/query-builder": "^0.74.33",
72
+ "@stacksjs/security": "0.74.33",
73
+ "@stacksjs/storage": "0.74.33",
74
+ "@stacksjs/strings": "0.74.33",
75
75
  "@stacksjs/ts-validation": "^0.5.6",
76
76
  "bun-query-builder": "^0.2.69",
77
77
  "dynamodb-tooling": "^0.3.2"
78
78
  },
79
79
  "devDependencies": {
80
- "@stacksjs/cli": "0.74.31",
81
- "@stacksjs/router": "0.74.31",
82
- "@stacksjs/utils": "0.74.31",
80
+ "@stacksjs/cli": "0.74.33",
81
+ "@stacksjs/router": "0.74.33",
82
+ "@stacksjs/utils": "0.74.33",
83
83
  "better-dx": "^0.2.24"
84
84
  }
85
85
  }