@fizzyflow/suisql
Version:
SuiSQL is a library and set of tools for working with decentralized SQL databases on the Sui blockchain and Walrus protocol.
456 lines (455 loc) • 14.3 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var SuiSql_exports = {};
__export(SuiSql_exports, {
default: () => SuiSql
});
module.exports = __toCommonJS(SuiSql_exports);
var import_SuiSqlStatement = __toESM(require("./SuiSqlStatement.js"));
var import_SuiSqlSync = __toESM(require("./SuiSqlSync.js"));
var import_SuiSqlField = __toESM(require("./SuiSqlField.js"));
var import_SuiSqlLibrarian = __toESM(require("./SuiSqlLibrarian.js"));
var import_SuiSqliteBinaryView = __toESM(require("./SuiSqliteBinaryView.js"));
var import_SuiSqlLog = __toESM(require("./SuiSqlLog.js"));
var State = /* @__PURE__ */ ((State2) => {
State2["INITIALIZING"] = "INITIALIZING";
State2["EMPTY"] = "EMPTY";
State2["ERROR"] = "ERROR";
State2["OK"] = "OK";
return State2;
})(State || {});
;
class SuiSql {
constructor(params) {
__publicField(this, "id");
__publicField(this, "name");
__publicField(this, "suiClient");
__publicField(this, "suiSqlSync");
__publicField(this, "state", "INITIALIZING" /* INITIALIZING */);
__publicField(this, "statements", []);
__publicField(this, "_db", null);
// private _SQL: initSqlJs.SqlJsStatic | null = null;
__publicField(this, "librarian", new import_SuiSqlLibrarian.default());
__publicField(this, "__initializationPromise", null);
__publicField(this, "paramsCopy");
__publicField(this, "mostRecentWriteChangeTime");
// time at which the most recent write operation was done
__publicField(this, "binaryView");
__publicField(this, "initialBinaryView");
this.paramsCopy = { ...params };
if (params.debug !== void 0) {
import_SuiSqlLog.default.switch(params.debug);
}
if (params.id && params.name) {
throw new Error("either id or name can be provided, not both");
}
if (params.id) {
this.id = params.id;
}
if (params.name) {
this.name = params.name;
}
if (params.suiClient) {
this.suiClient = params.suiClient;
if (this.id || this.name) {
this.suiSqlSync = new import_SuiSqlSync.default({
suiSql: this,
id: this.id,
name: this.name,
suiClient: this.suiClient,
signer: params.signer,
signAndExecuteTransaction: params.signAndExecuteTransaction,
currentWalletAddress: params.currentWalletAddress,
network: params.network,
walrusClient: params.walrusClient,
publisherUrl: params.publisherUrl,
aggregatorUrl: params.aggregatorUrl
});
}
} else {
throw new Error("SuiClient is required");
}
}
get network() {
if (this.suiSqlSync) {
return this.suiSqlSync.network;
}
return null;
}
/**
* DB Base Walrus Blob ID ( in base64 format, the one for urls )
*/
get walrusBlobId() {
if (this.suiSqlSync) {
return this.suiSqlSync.walrusBlobId;
}
return null;
}
get walrusEndEpoch() {
if (this.suiSqlSync) {
return this.suiSqlSync.walrusEndEpoch;
}
return null;
}
async hasWriteAccess() {
if (this.suiSqlSync) {
return await this.suiSqlSync.hasWriteAccess();
}
return false;
}
hasUnsavedChanges() {
if (this.suiSqlSync) {
return this.suiSqlSync.hasUnsavedChanges();
}
return false;
}
unsavedChangesCount() {
if (this.suiSqlSync) {
return this.suiSqlSync.unsavedChangesCount();
}
return 0;
}
getBinaryView() {
if (!this.binaryView || this.binaryView && this.mostRecentWriteChangeTime && (!this.binaryView.createdAt || this.binaryView.createdAt < this.mostRecentWriteChangeTime)) {
const data = this.export();
if (data) {
this.binaryView = new import_SuiSqliteBinaryView.default({
binary: data
});
}
}
if (this.binaryView) {
return this.binaryView;
}
return null;
}
async getBinaryPatch() {
const currentBinaryView = this.getBinaryView();
if (this.initialBinaryView && currentBinaryView) {
const binaryPatch = await currentBinaryView.getBinaryPatch(this.initialBinaryView);
return binaryPatch;
}
return null;
}
async getExpectedBlobId() {
const data = this.export();
if (data && this.suiSqlSync && this.suiSqlSync.walrus) {
return await this.suiSqlSync.walrus.calculateBlobId(data);
}
return null;
}
async applyBinaryPatch(binaryPatch) {
const currentBinaryView = this.getBinaryView();
if (!currentBinaryView) {
return false;
}
const patched = await currentBinaryView.getPatched(binaryPatch);
this.replace(patched);
return true;
}
async listDatabases(callback) {
if (this.suiSqlSync && this.suiSqlSync.chain) {
return await this.suiSqlSync.chain.listDatabases(callback);
}
return null;
}
/**
* Initialize a database re-using configuration of the current one, so only the id or name is required
* @param idOrName suiSql database id or name
*/
async database(idOrName) {
if (!this.paramsCopy) {
return null;
}
const paramsCopy = { ...this.paramsCopy };
if (idOrName.startsWith("0x")) {
paramsCopy.id = idOrName;
delete paramsCopy.name;
} else {
paramsCopy.name = idOrName;
delete paramsCopy.id;
}
const db = new SuiSql(paramsCopy);
await db.initialize();
return db;
}
get db() {
return this._db;
}
get writeExecutions() {
const ret = [];
for (const stmt of this.statements) {
ret.push(...stmt.writeExecutions);
}
return ret;
}
replace(data) {
if (this.librarian.isReady) {
this.binaryView = void 0;
this.initialBinaryView = new import_SuiSqliteBinaryView.default({
binary: data
});
;
this._db = this.librarian.fromBinarySync(data);
this.mostRecentWriteChangeTime = Date.now();
return true;
}
return false;
}
async initialize() {
if (this.__initializationPromise) {
return await this.__initializationPromise;
}
let __initializationPromiseResolver = () => {
};
this.__initializationPromise = new Promise((resolve) => {
__initializationPromiseResolver = resolve;
});
import_SuiSqlLog.default.log("initializing SuiSql database...", this.paramsCopy);
try {
this.state = "EMPTY" /* EMPTY */;
this._db = await this.librarian.fromBinary();
try {
if (this.suiSqlSync) {
await this.suiSqlSync.syncFromBlockchain();
this.id = this.suiSqlSync.id;
if (!this.id) {
import_SuiSqlLog.default.log("error initilizing");
this.state = "ERROR" /* ERROR */;
} else {
import_SuiSqlLog.default.log("db id", this.id);
this.mostRecentWriteChangeTime = Date.now();
if (this.suiSqlSync.hasBeenCreated) {
import_SuiSqlLog.default.log("database is freshly created");
this.state = "EMPTY" /* EMPTY */;
} else {
this.state = "OK" /* OK */;
}
let data = this.export();
if (data) {
if (data.length == 0) {
await this.touch();
const redata = this.export();
if (redata) {
data = redata;
}
}
this.initialBinaryView = new import_SuiSqliteBinaryView.default({
binary: data
});
}
}
}
} catch (e) {
import_SuiSqlLog.default.log("error", e);
this.state = "ERROR" /* ERROR */;
}
} catch (e) {
import_SuiSqlLog.default.log("error", e);
this.state = "ERROR" /* ERROR */;
}
__initializationPromiseResolver(this.state);
return this.state;
}
async sync(params) {
if (this.suiSqlSync) {
const success = await this.suiSqlSync.syncToBlockchain(params);
if (success) {
this.binaryView = void 0;
const data = this.export();
if (data) {
this.initialBinaryView = new import_SuiSqliteBinaryView.default({
binary: data
});
;
} else {
this.initialBinaryView = void 0;
}
}
} else {
throw new Error("not enough initialization params to sync");
}
}
async fillExpectedWalrus() {
if (this.suiSqlSync) {
await this.suiSqlSync.fillExpectedWalrus();
} else {
throw new Error("not enough initialization params to sync");
}
}
async extendWalrus(extendedEpochs = 1) {
await this.initialize();
if (this.suiSqlSync) {
await this.suiSqlSync.extendWalrus(extendedEpochs);
} else {
throw new Error("not enough initialization params to sync");
}
}
markAsOk() {
this.state = "OK" /* OK */;
}
/**
* Execute an SQL query, ignoring the rows it returns.
*/
async run(sql, params) {
import_SuiSqlLog.default.log("run", sql, params);
await this.initialize();
const suiSqlStatement = new import_SuiSqlStatement.default({
suiSql: this,
sql
});
this.statements.push(suiSqlStatement);
if (params != null) {
suiSqlStatement.bind(params);
}
suiSqlStatement.run();
return true;
}
/**
* Prepare an SQL statement
*
* @param {string} sql a string of SQL, that can contain placeholders (?, :VVV, :AAA, @AAA)
* @param {array|object} params values to bind to placeholders
*/
async prepare(sql, params) {
import_SuiSqlLog.default.log("prepare", sql, params);
await this.initialize();
const suiSqlStatement = new import_SuiSqlStatement.default({
suiSql: this,
sql
});
if (params != null) {
suiSqlStatement.bind(params);
}
this.statements.push(suiSqlStatement);
return suiSqlStatement;
}
/**
* Prepare an SQL statement and return all available results immediately
*
* @param {string} sql a string of SQL, that can contain placeholders (?, :VVV, :AAA, @AAA)
* @param {array|object} params values to bind to placeholders
*/
async query(sql, params) {
import_SuiSqlLog.default.log("query", sql, params);
await this.initialize();
const prepared = await this.prepare(sql, params);
const ret = [];
while (prepared.step()) {
ret.push(prepared.getAsObject());
}
import_SuiSqlLog.default.log("query results", ret);
return ret;
}
/**
* Run an sql text containing many sql queries, one by one, ignoring return data. Returns the count of processed queries.
*/
async iterateStatements(sql) {
import_SuiSqlLog.default.log("iterateStatements", sql);
await this.initialize();
if (!this.db) {
return 0;
}
let count = 0;
for (let statement of this.db.iterateStatements(sql)) {
const suiSqlStatement = new import_SuiSqlStatement.default({
suiSql: this,
statement
});
suiSqlStatement.step();
this.statements.push(suiSqlStatement);
count = count + 1;
}
return count;
}
async touch() {
if (!this.db) {
return false;
}
try {
const rawStatement = this.db.prepare("VACUUM;");
rawStatement.step();
rawStatement.free();
} catch (e) {
console.error(e);
}
return true;
}
async listTables() {
import_SuiSqlLog.default.log("listTables");
await this.initialize();
const tables = [];
const q = await this.prepare("SELECT name FROM sqlite_master WHERE type='table';");
while (q.step()) {
const row = q.getAsObject();
if (row) {
tables.push(row.name);
}
}
q.free();
import_SuiSqlLog.default.log("listTables results", tables);
return tables;
}
async describeTable(tableName) {
import_SuiSqlLog.default.log("describeTable", tableName);
await this.initialize();
const fields = [];
try {
const q = await this.prepare("select * from pragma_table_info(?) as tblInfo;", [tableName]);
await q.forEach((row) => {
fields.push(new import_SuiSqlField.default({
suiSql: this,
name: row.name,
type: row.type,
notnull: row.notnull,
dfltValue: row.dflt_value,
pk: row.pk,
cid: row.cid
}));
});
q.free();
} catch (e) {
console.error(e);
}
import_SuiSqlLog.default.log("describeTable results", fields);
return fields;
}
/**
* Export the database as SqlLite binary representation
*/
export() {
if (this.db) {
return this.db.export();
}
return null;
}
}
//# sourceMappingURL=SuiSql.js.map