class DatasetteClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
_getFetchURL(sql, params, _shape = "array") {
const searchParams = new URLSearchParams();
searchParams.append("sql", sql);
if (params)
for (const key in params) {
searchParams.append(key, params[key]);
}
searchParams.append("_shape", _shape);
return `${this.baseUrl}.json?${searchParams.toString()}`;
}
_parseTemplate(strings, ...values) {
let sql = "";
const params = {};
let paramsI = 0;
for (const [i, s] of strings.entries()) {
if (i < values.length) {
if (Array.isArray(values[i])) {
const sqlArrayParts = [];
for (const v of values[i]) {
const param = `p${paramsI++}`;
sqlArrayParts.push(`:${param}`);
params[param] = v;
}
sql += `${s}(${sqlArrayParts.join(",")})`;
}
else {
const param = `p${paramsI++}`;
sql += `${s}:${param}`;
params[param] = values[i];
}
} else {
sql += s;
}
}
return { sql, params };
}
_element(name, props, children) {
if (arguments.length === 2) (children = props), (props = undefined);
const element = document.createElement(name);
if (props !== undefined) for (const p in props) element[p] = props[p];
if (children !== undefined)
for (const c of children) element.appendChild(c);
return element;
}
_text(value) {
return document.createTextNode(value);
}
async query(query, params) {
const fetchUrl = this._getFetchURL(query, params);
const data = await fetch(fetchUrl).then((r) => r.json());
if (typeof data.ok !== "undefined" && !data.ok) throw Error(data.error);
return Object.assign(data, {
columns: data.length ? Object.keys(data[0]) : []
});
}
// have to redefine bc Datasette doesnt use non-named parameters ("?")
async describe(object) {
const rows = await (object === undefined
? this.query(`SELECT name FROM sqlite_master WHERE type = 'table'`)
: this.query(`SELECT * FROM pragma_table_info(:p0)`, { p0: object }));
if (!rows.length) throw new Error("Not found");
const { columns } = rows;
return this._element("table", { value: rows }, [
this._element("thead", [
this._element(
"tr",
columns.map((c) => this._element("th", [this._text(c)]))
)
]),
this._element(
"tbody",
rows.map((r) =>
this._element(
"tr",
columns.map((c) => this._element("td", [this._text(r[c])]))
)
)
)
]);
}
async sql(strings, ...values) {
const { sql, params } = this._parseTemplate(strings, ...values);
return this.query(sql, params);
}
}