Public
Script
Forked from std/sqlite
v12
April 3, 2024
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 { API_URL } from "https://esm.town/v/std/API_URL";
import { LibsqlError, type TransactionMode } from "npm:@libsql/client";
import { z } from "npm:zod";
/**
* Every Val Town account comes with its own private
* [SQLite database](https://www.sqlite.org/) that
* is accessible from any of your vals.
* ([Docs ↗](https://docs.val.town/std/sqlite))
*/
export const sqlite = {
/**
* Executes a SQLite statement.
*
* @param {InStatement} statement - The SQLite statement to execute.
* @example String query:
* `sqlite.execute("SELECT 1;")`
* @example Query with arguments:
* `sqlite.execute({sql: "SELECT * from books WHERE year > ?;", args: [2020]})`
*/
execute,
/**
* Executes a batch of SQLite statements.
*
* @param {InStatement[]} statements - An array of SQLite statements to execute.
* @param {TransactionMode} [mode] - The transaction mode for the batch execution.
*/
batch,
};
// ------------
// Functions
// ------------
async function execute(statement: InStatement): Promise<ResultSet> {
const res = await fetch(`${API_URL}/v1/sqlite/execute`, {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
body: JSON.stringify({ statement }),
});
if (!res.ok) {
throw createResError(await res.text());
}
return res.json();
}
async function batch(statements: InStatement[], mode?: TransactionMode): Promise<ResultSet[]> {
const res = await fetch(`${API_URL}/v1/sqlite/batch`, {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
body: JSON.stringify({ statements, mode }),
});
if (!res.ok) {
throw createResError(await res.text());
}
return res.json();
}
function createResError(body: string) {
try {
const e = zLibsqlError.parse(JSON.parse(body));
// e.message already contains the code, and LibsqlError adds the
// code to the beginning, so we remove it here
const msg = e.message.replace(e.code, "").replace(/^:\s+/, "");
return new LibsqlError(msg, e.code, e.rawCode);
} catch (_) {
// Failed to parse libsql error
}
return new Error(body);
}
// ------------
// Helpers
// ------------
const zLibsqlError = z.object({
message: z.string(),
code: z.string(),
rawCode: z.number().optional(),
});
// We patch these types to only support JSON values
export type InValue = null | string | number | boolean;
export type InArgs = Array<InValue> | Record<string, InValue>;
export type InStatement = {
/**
* The SQL statement to execute.
*/
sql: string;
/**
* The arguments to bind to the SQL statement.
*/
args: InArgs;
} | string;
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
v12
April 3, 2024
Probably don't need to explain what SQLite is here. I wonder if JSDoc has markdown rendering because then you could link to sqlite
Can drop Turso reference in the JSDoc string
I wonder if there's a proper JSDoc format for commenting TypeScript types.
Would be super neat if when you went to use sqlite.execute that you could inspect the type of InStatement without leaving your code editor.
Kinda on a slightly different topic. How about we export
execute
andbatch
? I think the only reason we didn't was because we couldn't at the timeI don't understand what you mean by this
I think the goal was to keep it consistent with libsql's API
Great! Ready to merge. Optional improvement ideas: