@shiyuhang0/serverless
Version:
TiDB Cloud Serverless Driver
344 lines (336 loc) • 9.61 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Connection: () => Connection,
DatabaseError: () => DatabaseError,
Tx: () => Tx,
connect: () => connect
});
module.exports = __toCommonJS(src_exports);
// src/format.ts
function format(query, values) {
return Array.isArray(values) ? replacePosition(query, values) : replaceNamed(query, values);
}
function replacePosition(query, values) {
let index = 0;
return query.replace(/\?/g, (match) => {
return index < values.length ? sanitize(values[index++]) : match;
});
}
function replaceNamed(query, values) {
return query.replace(/:(\w+)/g, (match, name) => {
return hasOwn(values, name) ? sanitize(values[name]) : match;
});
}
function hasOwn(obj, name) {
return Object.prototype.hasOwnProperty.call(obj, name);
}
function sanitize(value) {
if (value == null) {
return "null";
}
if (typeof value === "number") {
return String(value);
}
if (typeof value === "boolean") {
return value ? "true" : "false";
}
if (typeof value === "string") {
return quote(value);
}
if (Array.isArray(value)) {
return value.map(sanitize).join(", ");
}
if (value instanceof Date) {
return quote(value.toISOString().replace("Z", ""));
}
return quote(value.toString());
}
function quote(text) {
return `'${escape(text)}'`;
}
var re = /[\0\b\n\r\t\x1a\\"']/g;
function escape(text) {
return text.replace(re, replacement);
}
function replacement(text) {
switch (text) {
case '"':
return '\\"';
case "'":
return "\\'";
case "\n":
return "\\n";
case "\r":
return "\\r";
case " ":
return "\\t";
case "\\":
return "\\\\";
case "\0":
return "\\0";
case "\b":
return "\\b";
case "":
return "\\Z";
default:
return "";
}
}
// src/decode.ts
function cast(field, value, decoder) {
if (value === null) {
return null;
}
if (decoder[field.type]) {
return decoder[field.type](value);
}
switch (field.type) {
case "TINYINT":
case "UNSIGNED TINYINT":
case "SMALLINT":
case "UNSIGNED SMALLINT":
case "MEDIUMINT":
case "UNSIGNED MEDIUMINT":
case "INT":
case "UNSIGNED INT":
case "YEAR":
return parseInt(value, 10);
case "FLOAT":
case "DOUBLE":
return parseFloat(value);
case "BIGINT":
case "UNSIGNED BIGINT":
case "DECIMAL":
case "CHAR":
case "VARCHAR":
case "BINARY":
case "VARBINARY":
case "TINYTEXT":
case "TEXT":
case "MEDIUMTEXT":
case "LONGTEXT":
case "TINYBLOB":
case "BLOB":
case "MEDIUMBLOB":
case "LONGBLOB":
case "DATE":
case "TIME":
case "DATETIME":
case "TIMESTAMP":
case "BIT":
return value;
case "JSON":
return JSON.parse(value);
default:
return value;
}
}
// src/error.ts
var DatabaseError = class extends Error {
constructor(message, status, details) {
super(message);
this.status = status;
this.details = details;
}
};
// src/version.ts
var Version = "0.0.11-beta2";
// src/serverless.ts
async function postQuery(config, body, session = "", isolationLevel = null, debug) {
let fetchCacheOption = { cache: "no-store" };
try {
new Request("x:", fetchCacheOption);
} catch (err) {
fetchCacheOption = {};
}
const requestId = generateUniqueId();
if (debug) {
console.log(`[serverless-js debug] request id: ${requestId}`);
}
const url = new URL("/v1beta/sql", `https://http-${config.host}`);
const auth = btoa(`${config.username}:${config.password}`);
const { fetch: fetch2 } = config;
const database = config.database ?? "";
const headers = {
"Content-Type": "application/json",
"User-Agent": `serverless-js/${Version}`,
Authorization: `Basic ${auth}`,
"TiDB-Database": database,
"TiDB-Session": session,
"X-Debug-Trace-Id": requestId
};
if (isolationLevel) {
headers["TiDB-Isolation-Level"] = isolationLevel;
}
const response = await fetch2(url.toString(), {
method: "POST",
body,
headers,
...fetchCacheOption
});
if (debug) {
const traceId = response?.headers?.get("X-Debug-Trace-Id");
console.log(`[serverless-js debug] response id: ${traceId}`);
const contentEncoding = response?.headers?.get("Content-Encoding");
console.log(`[serverless-js debug] Content-Encoding: ${contentEncoding}`);
}
if (response.ok) {
const resp = await response.json();
const session2 = response.headers.get("TiDB-Session");
resp.session = session2 ?? "";
return resp;
} else {
let error;
try {
const e = await response.json();
error = new DatabaseError(e.message, response.status, e);
} catch {
error = new DatabaseError(response.statusText, response.status, null);
}
throw error;
}
}
function generateUniqueId() {
const datetime = (/* @__PURE__ */ new Date()).toISOString().replace(/[^\d]/g, "").slice(0, 14);
return `${datetime}${randomString(20)}`;
}
function randomString(n) {
let result = "";
const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
const l = characters.length;
for (let i = 0; i < n; i++) {
result += characters[Math.floor(Math.random() * l)];
}
return result;
}
// src/index.ts
var defaultExecuteOptions = {};
var Tx = class {
constructor(conn) {
this.conn = conn;
}
async execute(query, args = null, options = defaultExecuteOptions, txOptions = {}) {
return this.conn.execute(query, args, options, txOptions);
}
async commit() {
return this.conn.execute("COMMIT");
}
async rollback() {
return this.conn.execute("ROLLBACK");
}
};
var Connection = class _Connection {
constructor(config) {
var _a;
this.session = null;
this.config = { ...config };
if (typeof fetch !== "undefined") {
(_a = this.config).fetch || (_a.fetch = fetch);
}
if (config.url) {
const url = new URL(config.url);
if (!this.config.username) {
this.config.username = decodeURIComponent(url.username);
}
if (!this.config.password) {
this.config.password = decodeURIComponent(url.password);
}
if (!this.config.host) {
this.config.host = url.hostname;
}
if (!this.config.database) {
this.config.database = decodeURIComponent(url.pathname.slice(1));
}
}
}
getConfig() {
return this.config;
}
async begin(txOptions = {}) {
const conn = new _Connection(this.config);
const tx = new Tx(conn);
await tx.execute("BEGIN", void 0, void 0, txOptions);
return tx;
}
async execute(query, args = null, options = defaultExecuteOptions, txOptions = {}) {
const sql = args ? format(query, args) : query;
const body = JSON.stringify({ query: sql });
const debug = options.debug ?? this.config.debug ?? false;
if (debug) {
console.log(`[serverless-js debug] sql: ${sql}`);
}
const resp = await postQuery(
this.config,
body,
this.session ?? "",
sql == "BEGIN" ? txOptions.isolation : null,
debug
);
this.session = resp?.session ?? null;
if (this.session === null || this.session === "") {
throw new DatabaseError("empty session, please try again", 500, null);
}
const arrayMode = options.arrayMode ?? this.config.arrayMode ?? false;
const fullResult = options.fullResult ?? this.config.fullResult ?? false;
const decoders = { ...this.config.decoders, ...options.decoders };
const fields = resp?.types ?? [];
const rows = resp ? parse(fields, resp?.rows ?? [], cast, arrayMode, decoders) : [];
if (fullResult) {
const rowsAffected = resp?.rowsAffected ?? 0;
const lastInsertId = resp?.lastInsertID ?? null;
const typeByName = (acc, { name, type }) => ({ ...acc, [name]: type });
const types = fields.reduce(typeByName, {});
return {
statement: sql,
types,
rows,
rowsAffected,
lastInsertId,
rowCount: rows.length
};
}
return rows;
}
};
function connect(config) {
return new Connection(config);
}
function parseArrayRow(fields, rawRow, cast2, decoders) {
return fields.map((field, ix) => {
return cast2(field, rawRow[ix], decoders);
});
}
function parseObjectRow(fields, rawRow, cast2, decoders) {
return fields.reduce((acc, field, ix) => {
acc[field.name] = cast2(field, rawRow[ix], decoders);
return acc;
}, {});
}
function parse(fields, rows, cast2, arrayMode, decode) {
return rows.map((row) => arrayMode === true ? parseArrayRow(fields, row, cast2, decode) : parseObjectRow(fields, row, cast2, decode));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Connection,
DatabaseError,
Tx,
connect
});