Readme

sqliteUniverse: make queries against multiple vals or endpoints at the same time!

Example: @postpostscript/sqliteUniverseExample

Todo

  • tests‼️
  • update to support following syntax: SELECT * FROM "@example/endpoint".someTable or SELECT * FROM "@example/endpoint:::someTable"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { getValEndpointFromName } from "https://esm.town/v/postpostscript/meta";
import { getTableNames, replaceTableNames } from "https://esm.town/v/postpostscript/sqliteAST";
import { sqliteFromBlob } from "https://esm.town/v/postpostscript/sqliteBackup";
import { Statement, StatementInstance } from "https://esm.town/v/postpostscript/sqliteBuilder";
import { sqliteDump } from "https://esm.town/v/postpostscript/sqliteDump";
import { sqliteFromAPI } from "https://esm.town/v/postpostscript/sqliteFromAPI";
import type { SqliteInterface } from "https://esm.town/v/postpostscript/sqliteTypes";
import { createSqlite } from "https://esm.town/v/postpostscript/sqliteWasm";
import type { MaybePromise } from "https://esm.town/v/postpostscript/typeUtils";
import type { InStatement } from "https://esm.town/v/std/sqlite";
import type { ResultSet } from "npm:@libsql/client";
export const sqliteUniverse = {
execute,
batch,
};
export function sqliteUniverseWithOptions(options: SqliteUniverseOptions) {
return {
execute(statement: InStatement) {
return execute(statement, options);
},
batch(statements: InStatement[]) {
return batch(statements, options);
},
};
}
async function execute(
statement: InStatement,
options: SqliteUniverseOptions = {},
): Promise<Omit<ResultSet, "columnTypes" | "lastInsertRowid">> {
const [res] = await batch([statement], options);
return res;
}
async function batch(
statements: InStatement[],
options: SqliteUniverseOptions = {},
): Promise<Omit<ResultSet, "columnTypes" | "lastInsertRowid">[]> {
const endpointTableMap: EndpointTableMap = {};
const fullTableNames = statements.map(getTableNames).reduce((res, { tables }) => {
return new Set([...res, ...tables]);
}, new Set<string>());
fullTableNames.forEach(fullTable => {
const parts = fullTable.split("/");
const endpoint = parts.slice(0, -1).join("/");
const table = parts.slice(-1)[0];
endpointTableMap[endpoint] ??= new Set<string>();
endpointTableMap[endpoint].add(table);
});
const sqlite = await createSqliteFromEndpointTables(endpointTableMap, options);
const normalized = statements.map(statement => {
let { sql, args } = typeof statement === "string"
? new StatementInstance(statement)
: statement;
let match;
while (match = sql.match(/"([^"]+\/)sqlite_schema"/)) {
const [full, start] = match;
const index = sql.indexOf(full);
const numPreviousArgs = sql.slice(0, index).split("?").length - 1;
if (!args) {
args = [];
}
args.splice(numPreviousArgs, 0, `${start}%`);
sql = [
sql.slice(0, index),
`(SELECT * FROM sqlite_schema WHERE tbl_name LIKE ?)`,
sql.slice(index + full.length),
].join("");
}
return new StatementInstance(
sql,
args,
);
});
return sqlite.batch(normalized);
}
async function createSqliteFromEndpointTables(
endpointTableMap: EndpointTableMap,
{ interfaces = defaultInterfaces }: SqliteUniverseOptions = {},
) {
const schemas = Object.entries(endpointTableMap).map(async ([endpoint, tables]) => {
let sqlite = await interfaces.exact?.[endpoint];
if (sqlite instanceof Function) {
sqlite = await sqlite({ endpoint, tables });
}
for (let i = 0; !sqlite && i < interfaces.patterns?.length; i++) {
const [test, method] = interfaces.patterns[i];
const match = endpoint.match(test);
if (match) {
sqlite = await method({
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.