UNPKG

@libsql/sqlite3

Version:
227 lines (226 loc) 7.51 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Database = void 0; const node_events_1 = require("node:events"); const hrana = __importStar(require("@libsql/hrana-client")); const consts_js_1 = require("./consts.js"); const statement_js_1 = require("./statement.js"); class Database extends node_events_1.EventEmitter { // the ts-expect-errors are needed because the constructor may return early with another instance // @ts-expect-error #client; // @ts-expect-error #stream; // @ts-expect-error #serialize; // @ts-expect-error #waitingJobs; // @ts-expect-error #pendingPromises; constructor(url, ...args) { let callback; if (args.length >= 1 && typeof args[args.length - 1] === "function") { callback = args.pop(); } let mode = consts_js_1.OPEN_READWRITE | consts_js_1.OPEN_CREATE | consts_js_1.OPEN_FULLMUTEX; if (args.length === 1 && typeof args[0] === "number") { mode = args.shift(); } else if (args.length >= 1) { throw new TypeError("Invalid arguments"); } if (url.startsWith("file:")) { const sqlite3 = require("sqlite3"); return new sqlite3.Database(url, mode | consts_js_1.OPEN_URI, callback); } const parsedUrl = hrana.parseLibsqlUrl(url); super(); if (parsedUrl.hranaHttpUrl !== undefined) { this.#client = hrana.openHttp(parsedUrl.hranaHttpUrl, parsedUrl.authToken); } else { this.#client = hrana.openWs(parsedUrl.hranaWsUrl, parsedUrl.authToken); } this.#stream = this.#client.openStream(); this.#serialize = false; this.#pendingPromises = new Set(); this.#waitingJobs = []; this.#enqueueJob({ execute: () => this.#stream.queryValue("SELECT 1") .then(() => { if (callback !== undefined) { callback(null); } this.emit("open"); }) .catch((err) => { if (callback !== undefined) { callback(err); } else { this.emit("error", err); } }), serialize: true, }); } /** @private */ _enqueueStream(execute) { this.#enqueueJob({ execute: () => { return execute(this.#stream); }, serialize: this.#serialize, }); } /** @private */ _enqueue(execute) { this.#enqueueJob({ execute, serialize: this.#serialize, }); } #enqueueJob(job) { this.#waitingJobs.push(job); queueMicrotask(() => this.#pumpJobs()); } #pumpJobs() { let jobI = 0; while (jobI < this.#waitingJobs.length) { const job = this.#waitingJobs[jobI]; if (!job.serialize || this.#pendingPromises.size === 0) { this.#waitingJobs.splice(jobI, 1); const promise = job.execute(); this.#pendingPromises.add(promise); promise.finally(() => { this.#pendingPromises.delete(promise); this.#pumpJobs(); }); } else { jobI += 1; } } } close(callback) { this.#enqueueJob({ execute: () => { this.#client.close(); if (callback !== undefined) { callback(null); } this.emit("close"); return Promise.resolve(); }, serialize: true, }); } run(sql, ...args) { new statement_js_1.Statement(this, sql).run(...args); return this; } get(sql, ...args) { new statement_js_1.Statement(this, sql).get(...args); return this; } all(sql, ...args) { new statement_js_1.Statement(this, sql).all(...args); return this; } map(sql, ...args) { new statement_js_1.Statement(this, sql).map(...args); return this; } each(sql, ...args) { new statement_js_1.Statement(this, sql).each(...args); return this; } prepare(sql, ...args) { return new statement_js_1.Statement(this, sql).bind(...args); } on(event, listener) { return super.on(event, listener); } serialize(callback) { const old = this.#serialize; this.#serialize = true; if (callback !== undefined) { callback(); this.#serialize = old; } } parallelize(callback) { const old = this.#serialize; this.#serialize = false; if (callback !== undefined) { callback(); this.#serialize = old; } } wait(callback) { this._enqueue(() => { return Promise.resolve(null).then(callback); }); return this; } exec(sql, callback) { const executeSequence = async (stream) => { const version = await this.#client.getVersion(); if (version < 2) { throw new Error("Database.exec() is supported only with newer servers that implement version 2 " + "of the Hrana protocol"); } return await stream.sequence(sql); }; this._enqueueStream((stream) => executeSequence(stream) .then(() => { if (callback !== undefined) { callback.apply(this, [null]); } }) .catch((e) => { if (callback !== undefined) { callback.apply(this, [e]); } else { this.emit("error", e); } })); return this; } //configure(option: "busyTimeout", value: number): void; //configure(option: "limit", id: number, value: number): void; configure(option, ...args) { throw new Error(`${JSON.stringify(option)} is not a supported configuration option`); } loadExtension(filename, callback) { throw new Error("Database.loadExtension() is not implemented"); } interrupt() { throw new Error("Database.interrupt() is not implemented"); } } exports.Database = Database;