@libsql/client
Advanced tools
Comparing version 0.0.3 to 0.0.4
@@ -1,1 +0,1 @@ | ||
export * from './lib/libsql-js'; | ||
export * from "./lib/libsql-js"; |
@@ -1,4 +0,5 @@ | ||
import { ResultSet } from "../libsql-js"; | ||
import { BoundStatement, Params, ResultSet } from "../libsql-js"; | ||
export interface Driver { | ||
transaction(sql: string[]): Promise<ResultSet[]>; | ||
execute(stmt: string, params?: Params): Promise<ResultSet>; | ||
transaction(stmts: (string | BoundStatement)[]): Promise<ResultSet[]>; | ||
} |
@@ -1,7 +0,8 @@ | ||
import { ResultSet } from "../libsql-js"; | ||
import { ResultSet, BoundStatement, Params } from "../libsql-js"; | ||
import { Driver } from "./Driver"; | ||
export declare class HttpDriver implements Driver { | ||
url: URL; | ||
private url; | ||
constructor(url: URL); | ||
transaction(sql: string[]): Promise<ResultSet[]>; | ||
execute(stmt: string, params?: Params): Promise<ResultSet>; | ||
transaction(stmts: (string | BoundStatement)[]): Promise<ResultSet[]>; | ||
} |
@@ -38,2 +38,11 @@ "use strict"; | ||
}; | ||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { | ||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { | ||
if (ar || !(i in from)) { | ||
if (!ar) ar = Array.prototype.slice.call(from, 0, i); | ||
ar[i] = from[i]; | ||
} | ||
} | ||
return to.concat(ar || Array.prototype.slice.call(from)); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -49,23 +58,63 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
} | ||
HttpDriver.prototype.transaction = function (sql) { | ||
HttpDriver.prototype.execute = function (stmt, params) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var query, response, results; | ||
var rs; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
query = { | ||
statements: sql | ||
}; | ||
if (!(params === undefined)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, this.transaction([stmt])]; | ||
case 1: | ||
rs = (_a.sent())[0]; | ||
return [3 /*break*/, 4]; | ||
case 2: return [4 /*yield*/, this.transaction([{ sql: stmt, params: params }])]; | ||
case 3: | ||
rs = (_a.sent())[0]; | ||
_a.label = 4; | ||
case 4: return [2 /*return*/, rs]; | ||
} | ||
}); | ||
}); | ||
}; | ||
HttpDriver.prototype.transaction = function (stmts) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var statements, response, results, resultSets, rsIdx, result, rs, errorObj; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (stmts.length === 0) { | ||
return [2 /*return*/, []]; | ||
} | ||
statements = buildStatements(__spreadArray(__spreadArray(["BEGIN"], stmts, true), ["COMMIT"], false)); | ||
return [4 /*yield*/, (0, cross_fetch_1.default)(this.url, { | ||
method: 'POST', | ||
body: JSON.stringify(query), | ||
method: "POST", | ||
body: JSON.stringify(statements) | ||
})]; | ||
case 1: | ||
response = _a.sent(); | ||
if (!(response.status === 200)) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, response.json()]; | ||
case 2: | ||
results = _a.sent(); | ||
return [2 /*return*/, results.map(function (rs) { | ||
return rs.results; | ||
})]; | ||
validateTopLevelResults(results, statements.statements.length); | ||
resultSets = []; | ||
for (rsIdx = 1; rsIdx < results.length - 1; rsIdx++) { | ||
result = results[rsIdx]; | ||
rs = parseResultSet(result, rsIdx); | ||
// TODO duration needs to be provided by sqld | ||
rs.meta = { duration: 0 }; | ||
resultSets.push(rs); | ||
} | ||
return [2 /*return*/, resultSets]; | ||
case 3: return [4 /*yield*/, response.json()]; | ||
case 4: | ||
errorObj = _a.sent(); | ||
if (typeof errorObj === "object" && "error" in errorObj) { | ||
throw new Error(errorObj.error); | ||
} | ||
else { | ||
throw new Error("".concat(response.status, " ").concat(response.statusText)); | ||
} | ||
_a.label = 5; | ||
case 5: return [2 /*return*/]; | ||
} | ||
@@ -78,1 +127,76 @@ }); | ||
exports.HttpDriver = HttpDriver; | ||
function buildStatements(stmts) { | ||
var statements; | ||
if (typeof stmts[0] === "string") { | ||
statements = { statements: stmts }; | ||
} | ||
else { | ||
var s = stmts; | ||
statements = { | ||
statements: s.map(function (st) { | ||
return { q: st.sql, params: st.params }; | ||
}) | ||
}; | ||
} | ||
return statements; | ||
} | ||
function validateTopLevelResults(results, numResults) { | ||
if (!Array.isArray(results)) { | ||
throw new Error("Response JSON was not an array"); | ||
} | ||
if (results.length !== numResults) { | ||
throw new Error("Response array did not contain expected ".concat(numResults, " results")); | ||
} | ||
} | ||
function parseResultSet(result, rsIdx) { | ||
if (typeof result !== "object") { | ||
throw new Error("Result ".concat(rsIdx, " was not an object")); | ||
} | ||
var rs; | ||
if ("results" in result) { | ||
validateSuccessResult(result, rsIdx); | ||
rs = result.results; | ||
validateRowsAndCols(rs, rsIdx); | ||
checkSuccess(rs); | ||
rs.success = true; | ||
} | ||
else if ("error" in result) { | ||
validateErrorResult(result, rsIdx); | ||
rs = result; | ||
rs.success = false; | ||
} | ||
else { | ||
throw new Error("Result ".concat(rsIdx, " did not contain results or error")); | ||
} | ||
return rs; | ||
} | ||
function validateSuccessResult(result, rsIdx) { | ||
if (typeof result.results !== "object") { | ||
throw new Error("Result ".concat(rsIdx, " results was not an object")); | ||
} | ||
} | ||
// "success" currently just means rows and columns are present in the result. | ||
function checkSuccess(rs) { | ||
return Array.isArray(rs.rows) && Array.isArray(rs.columns); | ||
} | ||
// Check that the number of values in each row equals the number of columns. | ||
// | ||
// TODO this could go further by checking the typeof each value and looking | ||
// for inconsistencies among the rows. | ||
function validateRowsAndCols(r, rsIdx) { | ||
var numCols = r.columns.length; | ||
var rows = r.rows; | ||
for (var i = 0; i < rows.length; i++) { | ||
if (rows[i].length !== numCols) { | ||
throw new Error("Result ".concat(rsIdx, " row ").concat(i, " number of values != ").concat(numCols)); | ||
} | ||
} | ||
} | ||
function validateErrorResult(result, rsIdx) { | ||
if (typeof result.error !== "object") { | ||
throw new Error("Result ".concat(rsIdx, " results was not an object")); | ||
} | ||
if (typeof result.error.message !== "string") { | ||
throw new Error("Result ".concat(rsIdx, " error message was not a string")); | ||
} | ||
} |
@@ -1,9 +0,8 @@ | ||
import { Database } from "better-sqlite3"; | ||
import { ResultSet } from "../libsql-js"; | ||
import { BoundStatement, Params, ResultSet } from "../libsql-js"; | ||
import { Driver } from "./Driver"; | ||
export declare class SqliteDriver implements Driver { | ||
db: Database; | ||
private db; | ||
constructor(url: string); | ||
transaction(sqls: string[]): Promise<ResultSet[]>; | ||
execute(sql: string): Promise<ResultSet>; | ||
execute(sql: string, params?: Params): Promise<ResultSet>; | ||
transaction(stmts: (string | BoundStatement)[]): Promise<ResultSet[]>; | ||
} |
@@ -48,29 +48,4 @@ "use strict"; | ||
} | ||
SqliteDriver.prototype.transaction = function (sqls) { | ||
SqliteDriver.prototype.execute = function (sql, params) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var result, _i, sqls_1, sql, rs; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
result = []; | ||
_i = 0, sqls_1 = sqls; | ||
_a.label = 1; | ||
case 1: | ||
if (!(_i < sqls_1.length)) return [3 /*break*/, 4]; | ||
sql = sqls_1[_i]; | ||
return [4 /*yield*/, this.execute(sql)]; | ||
case 2: | ||
rs = _a.sent(); | ||
result.push(rs); | ||
_a.label = 3; | ||
case 3: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
}; | ||
SqliteDriver.prototype.execute = function (sql) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
@@ -80,26 +55,43 @@ return __generator(this, function (_a) { | ||
case 0: return [4 /*yield*/, new Promise(function (resolve) { | ||
var stmt = _this.db.prepare(sql); | ||
var columns; | ||
var rows; | ||
if (stmt.reader) { | ||
columns = stmt.columns().map(function (c) { return c.name; }); | ||
rows = stmt.all().map(function (row) { | ||
return columns.map(function (column) { return row[column]; }); | ||
try { | ||
var stmt = _this.db.prepare(sql); | ||
if (stmt.reader) { | ||
columns = stmt.columns().map(function (c) { return c.name; }); | ||
if (params === undefined) { | ||
rows = stmt.all(); | ||
} | ||
else { | ||
rows = stmt.all(params); | ||
} | ||
rows = rows.map(function (row) { | ||
return columns.map(function (column) { return row[column]; }); | ||
}); | ||
} | ||
else { | ||
columns = []; | ||
rows = []; | ||
if (params === undefined) { | ||
stmt.run(); | ||
} | ||
else { | ||
stmt.run(params); | ||
} | ||
} | ||
} | ||
catch (e) { | ||
resolve({ | ||
success: false, | ||
error: { message: e.message }, | ||
meta: { duration: 0 } | ||
}); | ||
return; | ||
} | ||
else { | ||
columns = []; | ||
rows = []; | ||
stmt.run(); | ||
} | ||
// FIXME: error handling | ||
var rs = { | ||
resolve({ | ||
success: true, | ||
columns: columns, | ||
rows: rows, | ||
success: true, | ||
meta: { | ||
duration: 0, | ||
}, | ||
}; | ||
resolve(rs); | ||
meta: { duration: 0 } | ||
}); | ||
})]; | ||
@@ -111,4 +103,37 @@ case 1: return [2 /*return*/, _a.sent()]; | ||
}; | ||
SqliteDriver.prototype.transaction = function (stmts) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var result, _i, stmts_1, stmt, rs; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
result = []; | ||
_i = 0, stmts_1 = stmts; | ||
_a.label = 1; | ||
case 1: | ||
if (!(_i < stmts_1.length)) return [3 /*break*/, 7]; | ||
stmt = stmts_1[_i]; | ||
rs = void 0; | ||
if (!(typeof stmt === "string")) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, this.execute(stmt)]; | ||
case 2: | ||
rs = _a.sent(); | ||
return [3 /*break*/, 5]; | ||
case 3: return [4 /*yield*/, this.execute(stmt.sql, stmt.params)]; | ||
case 4: | ||
rs = _a.sent(); | ||
_a.label = 5; | ||
case 5: | ||
result.push(rs); | ||
_a.label = 6; | ||
case 6: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 7: return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
}; | ||
return SqliteDriver; | ||
}()); | ||
exports.SqliteDriver = SqliteDriver; |
@@ -8,3 +8,9 @@ import { Driver } from "./driver/Driver"; | ||
*/ | ||
export type Row = Record<string, string | number | boolean | null>; | ||
export type Row = Record<string, SqlValue>; | ||
export type SqlValue = string | number | boolean | null; | ||
export type Params = SqlValue[] | Record<string, SqlValue>; | ||
export type BoundStatement = { | ||
sql: string; | ||
params: Params; | ||
}; | ||
/** | ||
@@ -15,17 +21,25 @@ * A SQL query result set. | ||
/** | ||
* Was the query successful? | ||
* If true, rows and columns are provided. | ||
* If false, error is provided | ||
*/ | ||
success: boolean; | ||
/** | ||
* Query result columns. | ||
*/ | ||
columns: string[] | null; | ||
columns?: string[]; | ||
/** | ||
* Query results. | ||
*/ | ||
rows: Row[] | null; | ||
rows?: Row[]; | ||
/** | ||
* Was the query successful? | ||
* Error information, if not successful. | ||
*/ | ||
success: boolean; | ||
error?: { | ||
message: string; | ||
}; | ||
/** | ||
* Extra information about the query results. | ||
*/ | ||
meta: { | ||
/** | ||
* Time duration to execute the query. | ||
*/ | ||
duration: number; | ||
@@ -38,3 +52,3 @@ }; | ||
export declare class Connection { | ||
driver: Driver; | ||
private driver; | ||
constructor(driver: Driver); | ||
@@ -44,8 +58,8 @@ /** | ||
*/ | ||
execute(sql: string): Promise<ResultSet>; | ||
execute(sql: string, params?: Params): Promise<ResultSet>; | ||
/** | ||
* Execute a batch of SQL statements in a transaction. | ||
*/ | ||
transaction(stmts: string[]): Promise<ResultSet[]>; | ||
transaction(stmts: string[] | BoundStatement[]): Promise<ResultSet[]>; | ||
} | ||
export declare function connect(config: Config): Connection; |
@@ -52,12 +52,6 @@ "use strict"; | ||
*/ | ||
Connection.prototype.execute = function (sql) { | ||
Connection.prototype.execute = function (sql, params) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var results; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.transaction([sql])]; | ||
case 1: | ||
results = _a.sent(); | ||
return [2 /*return*/, results[0]]; | ||
} | ||
return [2 /*return*/, this.driver.execute(sql, params)]; | ||
}); | ||
@@ -64,0 +58,0 @@ }); |
{ | ||
"name": "@libsql/client", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"keywords": [ | ||
@@ -25,3 +25,4 @@ "libsql", | ||
"build": "tsc", | ||
"test": "jest --config jestconfig.json" | ||
"test": "jest --config jestconfig.json", | ||
"prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write" | ||
}, | ||
@@ -35,2 +36,3 @@ "files": [ | ||
"jest": "^29.3.1", | ||
"prettier": "^2.8.3", | ||
"ts-jest": "^29.0.3", | ||
@@ -37,0 +39,0 @@ "typescript": "^4.9.4" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25136
522
6