@@ -34,6 +34,29 @@ import { resolveAuthSecret, resolveBaseUrl } from './boot-env.js';
3434import type { AppBundleResolver } from './project-kernel-factory.js' ;
3535import { createObjectOSStack } from './objectos-stack.js' ;
3636
37+ /**
38+ * Infer the storage driver type from a database connection-URL scheme.
39+ * Returns `''` if the URL is empty or the scheme is unrecognised.
40+ *
41+ * mongodb://, mongodb+srv:// → 'mongodb'
42+ * postgres://, postgresql:// → 'postgres'
43+ * mysql://, mysql2:// → 'mysql'
44+ * libsql://, https://*.turso.* → 'turso'
45+ * file:, sqlite:, :memory:, *.db,
46+ * *.sqlite, *.sqlite3 → 'sqlite'
47+ */
48+ function inferDriverFromUrl ( url : string | undefined ) : string {
49+ if ( ! url ) return '' ;
50+ const u = url . trim ( ) ;
51+ if ( / ^ m o n g o d b ( \+ s r v ) ? : \/ \/ / i. test ( u ) ) return 'mongodb' ;
52+ if ( / ^ p o s t g r e s ( q l ) ? : \/ \/ / i. test ( u ) ) return 'postgres' ;
53+ if ( / ^ m y s q l 2 ? : \/ \/ / i. test ( u ) ) return 'mysql' ;
54+ if ( / ^ l i b s q l : \/ \/ / i. test ( u ) ) return 'turso' ;
55+ if ( / ^ h t t p s ? : \/ \/ / i. test ( u ) && / \. t u r s o \. / i. test ( u ) ) return 'turso' ;
56+ if ( / ^ f i l e : / i. test ( u ) || / ^ s q l i t e : / i. test ( u ) || u === ':memory:' || / \. ( d b | s q l i t e | s q l i t e 3 ) $ / i. test ( u ) ) return 'sqlite' ;
57+ return '' ;
58+ }
59+
3760/**
3861 * Default ObjectStack Cloud base URL — the local `apps/cloud` instance
3962 * running on port 4000. Override via `OS_CLOUD_URL` (or
@@ -124,8 +147,22 @@ export async function createRuntimeStack(config?: RuntimeStackConfig): Promise<R
124147 const dataDir = cfg . dataDir ?? resolvePath ( cwd , '.objectstack/data' ) ;
125148 mkdirSync ( dataDir , { recursive : true } ) ;
126149
127- const controlDbUrl = `file:${ resolvePath ( dataDir , 'control.db' ) } ` ;
128- const projectDbUrl = `file:${ resolvePath ( dataDir , `${ projectId } .db` ) } ` ;
150+ // Control-plane DB. In single-project local mode this is the framework's
151+ // bookkeeping DB (sys_organization / sys_project / …). It defaults to a
152+ // local SQLite file. Users can override with `OS_CONTROL_DATABASE_URL`
153+ // (preferred); `OS_DATABASE_URL` is reserved for the *project's* data.
154+ const controlDbUrl = process . env . OS_CONTROL_DATABASE_URL ?. trim ( )
155+ || `file:${ resolvePath ( dataDir , 'control.db' ) } ` ;
156+
157+ // Project DB. This is the user's business-data DB. When `OS_DATABASE_URL`
158+ // is set, honour it (and infer the driver from its scheme unless
159+ // `OS_DATABASE_DRIVER` overrides). Otherwise fall back to a local
160+ // SQLite file beside `control.db`.
161+ const envProjectDbUrl = process . env . OS_DATABASE_URL ?. trim ( ) ;
162+ const projectDbUrl = envProjectDbUrl || `file:${ resolvePath ( dataDir , `${ projectId } .db` ) } ` ;
163+ const projectDbDriver = ( process . env . OS_DATABASE_DRIVER ?. trim ( ) . toLowerCase ( ) )
164+ || inferDriverFromUrl ( projectDbUrl )
165+ || 'sqlite' ;
129166
130167 const authSecret = cfg . authSecret ?? resolveAuthSecret ( ) ;
131168 const baseUrl = cfg . baseUrl ?? resolveBaseUrl ( ) ;
@@ -176,7 +213,7 @@ export async function createRuntimeStack(config?: RuntimeStackConfig): Promise<R
176213 createSingleProjectPlugin ( {
177214 projectId,
178215 projectDatabaseUrl : projectDbUrl ,
179- projectDatabaseDriver : 'sqlite' ,
216+ projectDatabaseDriver : projectDbDriver ,
180217 apiPrefix : cfg . apiPrefix ,
181218 } ) ,
182219 ) ;
0 commit comments