@@ -427,6 +427,25 @@ function urlSchemeOf(dbUrl: string): string | undefined {
427427 return / ^ [ a - z ] [ a - z 0 - 9 + . - ] * : \/ \/ / i. exec ( dbUrl ) ?. [ 0 ] ;
428428}
429429
430+ /**
431+ * The scheme a database URL opens with — its `<scheme>://` ({@link urlSchemeOf})
432+ * or an OPAQUE scheme, two or more characters and a `:` with no `//`
433+ * (`jdbc:`, `redis:`) — or `undefined` when it has neither and so is a path.
434+ *
435+ * A single letter and a `:` is a Windows drive (`C:\data\app.db`,
436+ * `C:/data/app.db`), never a scheme. An opaque scheme ends at its FIRST `:`, so
437+ * like a `<scheme>://` it cannot hold a userinfo, a host or a path, and a
438+ * refusal may name it. ONE answer for the two places this host asks whether a
439+ * value is a path: the URL classification ({@link detectDriverFromUrl}) and the
440+ * SQLite filename rule an explicit selection meets instead
441+ * ({@link sqliteFilenameFromUrl}). The CLI host draws the same line
442+ * (`inferDriverTypeFromUrl` in `utils/storage-driver.ts`), and
443+ * `driver-vocabulary-parity.test.ts` holds the hosts equal.
444+ */
445+ function schemeOf ( dbUrl : string ) : string | undefined {
446+ return urlSchemeOf ( dbUrl ) ?? / ^ [ a - z ] [ a - z 0 - 9 + . - ] + : / i. exec ( dbUrl ) ?. [ 0 ] ;
447+ }
448+
430449function detectDriverFromUrl ( dbUrl : string ) : ResolvedDriverKind {
431450 // The in-memory (mingo) engine's two schemes — character-for-character the
432451 // CLI's `inferDriverTypeFromUrl` regex, for the reason the mysql and turso
@@ -473,7 +492,7 @@ function detectDriverFromUrl(dbUrl: string): ResolvedDriverKind {
473492 // `C:/data/app.db`), not a scheme, and stays a path. The CLI host draws the
474493 // same line (`inferDriverTypeFromUrl` in `utils/storage-driver.ts`), and
475494 // `driver-vocabulary-parity.test.ts` holds the two equal.
476- const scheme = urlSchemeOf ( dbUrl ) ?? / ^ [ a - z ] [ a - z 0 - 9 + . - ] + : / i . exec ( dbUrl ) ?. [ 0 ] ;
495+ const scheme = schemeOf ( dbUrl ) ;
477496 // Bare path without a scheme — treat as a sqlite file path.
478497 if ( ! scheme ) return wasmFile ? 'sqlite-wasm' : 'sqlite' ;
479498 // Names the scheme, never the URL ({@link urlSchemeOf}). An opaque scheme
@@ -603,8 +622,32 @@ function refuseRetiredMemoryEngine(
603622 * URL→filename for the two sqlite kinds: the ONE rule both hosts turn a SQLite
604623 * database URL into a file with. Throws on a URL that isn't a path.
605624 *
606- * Takes the URL as {@link resolveStandaloneDatabase} has it, i.e. after
607- * `normalizeDatabaseUrl` (`sqlite:` / `sqlite://` are already `file:`).
625+ * Takes the URL as {@link resolveStandaloneDatabase} has it. The explicit and
626+ * `OS_DATABASE_URL` rungs have applied `normalizeDatabaseUrl` (`sqlite:` /
627+ * `sqlite://` are `file:` there); a declared datasource's `url` and
628+ * `TURSO_DATABASE_URL` arrive as written, so on those two rungs a `sqlite:`
629+ * alias is a scheme here and is refused.
630+ *
631+ * An explicit `sqlite` / `sqlite-wasm` selection skips
632+ * {@link detectDriverFromUrl}, so this rule is the only check its URL meets, and
633+ * on BOTH kinds it refuses every value that is not a file path:
634+ * - a `<scheme>://` left after the strip below: a server URL
635+ * (`postgres://user:password@…`) or a scheme nothing recognises
636+ * (`redis://h/x.db`);
637+ * - an OPAQUE scheme ({@link schemeOf}: two or more characters and a `:` with
638+ * no `//`, as in `jdbc:postgresql://…` or `redis:h`), judged on the value as
639+ * given. A value whose `file:` (or, on the WASM kind, `wasm-sqlite:`) prefix
640+ * the strip removed is a file, colons and all: `file:backup:2026.db` names
641+ * `backup:2026.db`, while `backup:2026.db` is the scheme `backup:`.
642+ * Both used to pass through as a file name, and the boot made directories out of
643+ * the URL: `./jdbc:postgresql:/127.0.0.1:5999/`, and on the WASM kind, which
644+ * refused nothing, `./postgres:/USER:SECRET@127.0.0.1:5999/`, the credentials
645+ * in a directory name. The refusal comes before any directory is made, and it
646+ * names the selected kind and the scheme, never the URL.
647+ *
648+ * An empty name is `:memory:` on the WASM kind; on the `sqlite` kind a `file:`
649+ * that names no file is refused.
650+ *
608651 * `file:` and `file://` are stripped case-insensitively on BOTH kinds, as
609652 * {@link detectDriverFromUrl} classifies them (`/^file:/i`): a prefix the
610653 * classifier accepts and this strip kept became part of the filename, so
@@ -618,21 +661,21 @@ function refuseRetiredMemoryEngine(
618661 * `//NAME` at the filesystem root there while this host opened `NAME`.
619662 */
620663export function sqliteFilenameFromUrl ( dbUrl : string , kind : 'sqlite' | 'sqlite-wasm' ) : string {
621- if ( kind === 'sqlite-wasm' ) {
622- return dbUrl
623- . replace ( / ^ w a s m - s q l i t e : ( \/ \/ ) ? / i, '' )
624- . replace ( / ^ f i l e : ( \/ \/ ) ? / i , ' ') || ':memory:' ;
625- }
626- const filename = dbUrl . replace ( / ^ f i l e : ( \/ \/ ) ? / i , '' ) ;
627- const scheme = urlSchemeOf ( filename ) ;
664+ const filename = kind === 'sqlite-wasm'
665+ ? dbUrl . replace ( / ^ w a s m - s q l i t e : ( \/ \/ ) ? / i , '' ) . replace ( / ^ f i l e : ( \/ \/ ) ? / i , '' )
666+ : dbUrl . replace ( / ^ f i l e : ( \/ \/ ) ? / i, '' ) ;
667+ if ( ! filename && kind === 'sqlite-wasm ') return ':memory:' ;
668+ // A prefix the strip removed made the value a file, so only a value left as
669+ // given is asked for an opaque scheme (` file:` is one itself).
670+ const scheme = filename === dbUrl ? schemeOf ( dbUrl ) : urlSchemeOf ( filename ) ;
628671 if ( ! filename || scheme ) {
629- // Names the scheme, never the URL ({@link urlSchemeOf }). This is the
630- // refusal an explicit `sqlite` selection meets over a server URL
672+ // Names the scheme, never the URL ({@link schemeOf }). This is the
673+ // refusal an explicit selection meets over a server URL
631674 // (`OS_DATABASE_DRIVER=sqlite` beside a `postgres://user:password@…`),
632675 // so the URL it would otherwise echo is exactly the credentialed kind.
633676 const why = scheme ? `its scheme is "${ scheme } "` : 'it names no file after "file:"' ;
634677 throw new Error (
635- `[StandaloneStack] sqlite driver was selected but the URL does not look like a file path (${ why } ). ` +
678+ `[StandaloneStack] ${ kind } driver was selected but the URL does not look like a file path (${ why } ). ` +
636679 `Use file:/path/to/db.sqlite, or set OS_DATABASE_DRIVER explicitly.`
637680 ) ;
638681 }
0 commit comments