var symbolFor = Symbol.for; const lazy = globalThis[symbolFor("Bun.lazy")]; if (!lazy || typeof lazy !== "function") { throw new Error( "Something went wrong while loading Bun. Expected 'Bun.lazy' to be defined.", ); } var defineProperties = Object.defineProperties; var toStringTag = Symbol.toStringTag; var apply = Function.prototype.apply; var isArray = Array.isArray; var isTypedArray = ArrayBuffer.isView; export const constants = { SQLITE_OPEN_READONLY: 0x00000001 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_READWRITE: 0x00000002 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_CREATE: 0x00000004 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_DELETEONCLOSE: 0x00000008 /* VFS only */, SQLITE_OPEN_EXCLUSIVE: 0x00000010 /* VFS only */, SQLITE_OPEN_AUTOPROXY: 0x00000020 /* VFS only */, SQLITE_OPEN_URI: 0x00000040 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_MEMORY: 0x00000080 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_MAIN_DB: 0x00000100 /* VFS only */, SQLITE_OPEN_TEMP_DB: 0x00000200 /* VFS only */, SQLITE_OPEN_TRANSIENT_DB: 0x00000400 /* VFS only */, SQLITE_OPEN_MAIN_JOURNAL: 0x00000800 /* VFS only */, SQLITE_OPEN_TEMP_JOURNAL: 0x00001000 /* VFS only */, SQLITE_OPEN_SUBJOURNAL: 0x00002000 /* VFS only */, SQLITE_OPEN_SUPER_JOURNAL: 0x00004000 /* VFS only */, SQLITE_OPEN_NOMUTEX: 0x00008000 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_FULLMUTEX: 0x00010000 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_SHAREDCACHE: 0x00020000 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_PRIVATECACHE: 0x00040000 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_WAL: 0x00080000 /* VFS only */, SQLITE_OPEN_NOFOLLOW: 0x01000000 /* Ok for sqlite3_open_v2() */, SQLITE_OPEN_EXRESCODE: 0x02000000 /* Extended result codes */, SQLITE_PREPARE_PERSISTENT: 0x01, SQLITE_PREPARE_NORMALIZE: 0x02, SQLITE_PREPARE_NO_VTAB: 0x04, }; var SQL; var _SQL; var controllers; export class Statement { constructor(raw) { this.#raw = raw; switch (raw.paramsCount) { case 0: { this.get = this.#getNoArgs; this.all = this.#allNoArgs; this.values = this.#valuesNoArgs; this.run = this.#runNoArgs; break; } default: { this.get = this.#get; this.all = this.#all; this.values = this.#values; this.run = this.#run; break; } } } #raw; get; all; values; run; isFinalized = false; toJSON() { return { sql: this.native.toString(), isFinalized: this.isFinalized, paramsCount: this.paramsCount, columnNames: this.columnNames, }; } get [toStringTag]() { return `"${this.native.toString()}"`; } toString() { return this.native.toString(); } get native() { return this.#raw; } #getNoArgs() { return this.#raw.get(); } #allNoArgs() { return this.#raw.all(); } #valuesNoArgs() { return this.#raw.values(); } #runNoArgs() { this.#raw.run(); } #get(...args) { if (args.length === 0) return this.#getNoArgs(); var arg0 = args[0]; // ["foo"] => ["foo"] // ("foo") => ["foo"] // (Uint8Array(1024)) => [Uint8Array] // (123) => [123] return !isArray(arg0) && (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) ? this.#raw.get(args) : this.#raw.get(...args); } #all(...args) { if (args.length === 0) return this.#allNoArgs(); var arg0 = args[0]; // ["foo"] => ["foo"] // ("foo") => ["foo"] // (Uint8Array(1024)) => [Uint8Array] // (123) => [123] return !isArray(arg0) && (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) ? this.#raw.all(args) : this.#raw.all(...args); } #values(...args) { if (args.length === 0) return this.#valuesNoArgs(); var arg0 = args[0]; // ["foo"] => ["foo"] // ("foo") => ["foo"] // (Uint8Array(1024)) => [Uint8Array] // (123) => [123] return !isArray(arg0) && (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) ? this.#raw.values(args) : this.#raw.values(...args); } #run(...args) { if (args.length === 0) return this.#runNoArgs(); var arg0 = args[0]; !isArray(arg0) && (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) ? this.#raw.run(args) : this.#raw.run(...args); } get columnNames() { return this.#raw.columns; } get paramsCount() { return this.#raw.paramsCount; } finalize(...args) { this.isFinalized = true; return this.#raw.finalize(...args); } } var cachedCount = symbolFor("Bun.Database.cache.count"); export class Database { constructor(filenameGiven, options) { if (typeof filenameGiven === "undefined") { } else if (typeof filenameGiven !== "string") { if (isTypedArray(filenameGiven)) { this.#handle = Database.deserialize( filenameGiven, typeof options === "object" && options ? !!options.readonly : ((options | 0) & constants.SQLITE_OPEN_READONLY) != 0, ); this.filename = ":memory:"; return; } throw new TypeError( `Expected 'filename' to be a string, got '${typeof filenameGiven}'`, ); } var filename = typeof filenameGiven === "string" ? filenameGiven.trim() : ":memory:"; var flags = constants.SQLITE_OPEN_READWRITE | constants.SQLITE_OPEN_CREATE; if (typeof options === "object" && options) { flags = 0; if (options.readonly) { flags = constants.SQLITE_OPEN_READONLY; } if ("readOnly" in options) throw new TypeError( 'Misspelled option "readOnly" should be "readonly"', ); if (options.create) { flags = constants.SQLITE_OPEN_READWRITE | constants.SQLITE_OPEN_CREATE; } if (options.readwrite) { flags |= constants.SQLITE_OPEN_READWRITE; } } else if (typeof options === "number") { flags = options; } const anonymous = filename === "" || filename === ":memory:"; if (anonymous && (flags & constants.SQLITE_OPEN_READONLY) !== 0) { throw new Error("Cannot open an anonymous database in read-only mode."); } if (!SQL) { _SQL = SQL = lazy("sqlite"); } this.#handle = SQL.open(anonymous ? ":memory:" : filename, flags); this.filename = filename; } #handle; #cachedQueriesKeys = []; #cachedQueriesLengths = []; #cachedQueriesValues = []; filename; get handle() { return this.#handle; } get inTransaction() { return SQL.isInTransaction(this.#handle); } static open(filename, options) { return new Database(filename, options); } loadExtension(name, entryPoint) { return SQL.loadExtension(this.#handle, name, entryPoint); } serialize(optionalName) { return SQL.serialize(this.#handle, optionalName || "main"); } static deserialize(serialized, isReadOnly = false) { if (!SQL) { _SQL = SQL = lazy("sqlite"); } return SQL.deserialize(serialized, isReadOnly); } static setCustomSQLite(path) { if (!SQL) { _SQL = SQL = lazy("sqlite"); } return SQL.setCustomSQLite(path); } close() { this.clearQueryCache(); return SQL.close(this.#handle); } clearQueryCache() { for (let item of this.#cachedQueriesValues) { item.finalize(); } this.#cachedQueriesKeys.length = 0; this.#cachedQueriesValues.length = 0; this.#cachedQueriesLengths.length = 0; } run(query, ...params) { if (params.length === 0) { SQL.run(this.#handle, query); return; } var arg0 = params[0]; return !isArray(arg0) && (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) ? SQL.run(this.#handle, query, params) : SQL.run(this.#handle, query, ...params); } prepare(query, params, flags) { return new Statement(SQL.prepare(this.#handle, query, params, flags || 0)); } static MAX_QUERY_CACHE_SIZE = 20; get [cachedCount]() { return this.#cachedQueriesKeys.length; } query(query) { if (typeof query !== "string") { throw new TypeError( `Expected 'query' to be a string, got '${typeof query}'`, ); } if (query.length === 0) { throw new Error("SQL query cannot be empty."); } // this list should be pretty small var index = this.#cachedQueriesLengths.indexOf(query.length); while (index !== -1) { if (this.#cachedQueriesKeys[index] !== query) { index = this.#cachedQueriesLengths.indexOf(query.length, index + 1); continue; } var stmt = this.#cachedQueriesValues[index]; if (stmt.isFinalized) { return (this.#cachedQueriesValues[index] = this.prepare( query, undefined, willCache ? constants.SQLITE_PREPARE_PERSISTENT : 0, )); } return stmt; } const willCache = this.#cachedQueriesKeys.length < Database.MAX_QUERY_CACHE_SIZE; var stmt = this.prepare( query, undefined, willCache ? constants.SQLITE_PREPARE_PERSISTENT : 0, ); if (willCache) { this.#cachedQueriesKeys.push(query); this.#cachedQueriesLengths.push(query.length); this.#cachedQueriesValues.push(stmt); } return stmt; } // Code for transactions is largely copied from better-sqlite3 // https://github.com/JoshuaWise/better-sqlite3/blob/master/lib/methods/transaction.js // thank you @JoshuaWise! transaction(fn, self) { if (typeof fn !== "function") throw new TypeError("Expected first argument to be a function"); const db = this; const controller = getController(db, self); // Each version of the transaction function has these same properties const properties = { default: { value: wrapTransaction(fn, db, controller.default) }, deferred: { value: wrapTransaction(fn, db, controller.deferred) }, immediate: { value: wrapTransaction(fn, db, controller.immediate), }, exclusive: { value: wrapTransaction(fn, db, controller.exclusive), }, database: { value: this, enumerable: true }, }; defineProperties(properties.default.value, properties); defineProperties(properties.deferred.value, properties); defineProperties(properties.immediate.value, properties); defineProperties(properties.exclusive.value, properties); // Return the default version of the transaction function return properties.default.value; } } Database.prototype.exec = Database.prototype.run; // Return the database's cached transaction controller, or create a new one const getController = (db, self) => { let controller = (controllers ||= new WeakMap()).get(db); if (!controller) { const shared = { commit: db.prepare("COMMIT", undefined, 0), rollback: db.prepare("ROLLBACK", undefined, 0), savepoint: db.prepare("SAVEPOINT `\t_bs3.\t`", undefined, 0), release: db.prepare("RELEASE `\t_bs3.\t`", undefined, 0), rollbackTo: db.prepare("ROLLBACK TO `\t_bs3.\t`", undefined, 0), }; controllers.set( db, (controller = { default: Object.assign( { begin: db.prepare("BEGIN", undefined, 0) }, shared, ), deferred: Object.assign( { begin: db.prepare("BEGIN DEFERRED", undefined, 0) }, shared, ), immediate: Object.assign( { begin: db.prepare("BEGIN IMMEDIATE", undefined, 0) }, shared, ), exclusive: Object.assign( { begin: db.prepare("BEGIN EXCLUSIVE", undefined, 0) }, shared, ), }), ); } return controller; }; // Return a new transaction function by wrapping the given function const wrapTransaction = ( fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }, ) => function transaction(...args) { let before, after, undo; if (db.inTransaction) { before = savepoint; after = release; undo = rollbackTo; } else { before = begin; after = commit; undo = rollback; } try { before.run(); const result = fn.apply(this, args); after.run(); return result; } catch (ex) { if (db.inTransaction) { undo.run(); if (undo !== rollback) after.run(); } throw ex; } }; export { _SQL as native }; export { Database as default };