Skip to content

Commit 192dfb5

Browse files
committed
Add transaction support, update readme
1 parent eade31d commit 192dfb5

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ Zen for Node.js 16+ is compatible with:
6262
*[`postgres`](https://www.npmjs.com/package/postgres) 3.x
6363
*[`@clickhouse/client`](https://www.npmjs.com/package/@clickhouse/client) 1.x
6464
*[`@prisma/client`](https://www.npmjs.com/package/@prisma/client) 5.x
65+
*[`@libsql/client`](https://www.npmjs.com/package/@libsql/client) ^0.10.x
66+
*[`libsql`](https://www.npmjs.com/package/libsql) ^0.4.x
6567

6668
### Cloud providers
6769

library/sinks/LibSQLClient.test.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ const safeContext: Context = {
3131
route: "/posts/:id",
3232
};
3333

34+
const testOpts = { skip: !global.Request ? "fetch is not available" : false };
35+
3436
const agent = createTestAgent();
3537
agent.start([new LibSQLClient()]);
3638

37-
t.test("it works with @libsql/client: in-memory", async (t) => {
39+
t.test("it works with @libsql/client: in-memory", testOpts, async (t) => {
3840
const { createClient } =
3941
require("@libsql/client") as typeof import("@libsql/client");
4042

@@ -166,6 +168,36 @@ t.test("it works with @libsql/client: in-memory", async (t) => {
166168
"Cannot read properties of null (reading 'map')"
167169
);
168170
}
171+
172+
const transaction = await client.transaction("write");
173+
174+
const error9 = await t.rejects(() =>
175+
transaction.execute("SELECT 1;-- should be blocked")
176+
);
177+
t.ok(error9 instanceof Error);
178+
if (error9 instanceof Error) {
179+
t.same(
180+
error9.message,
181+
"Zen has blocked an SQL injection: @libsql/client.transaction.execute(...) originating from body.myTitle"
182+
);
183+
}
184+
185+
const error10 = await t.rejects(() =>
186+
transaction.batch(["SELECT 1;-- should be blocked"])
187+
);
188+
t.ok(error10 instanceof Error);
189+
if (error10 instanceof Error) {
190+
t.same(
191+
error10.message,
192+
"Zen has blocked an SQL injection: @libsql/client.transaction.batch(...) originating from body.myTitle"
193+
);
194+
}
195+
196+
await transaction.commit();
197+
});
198+
199+
await runWithContext(safeContext, async () => {
200+
await client.execute("SELECT 1;-- This is a comment");
169201
});
170202
} catch (error: any) {
171203
t.fail(error);
@@ -174,7 +206,7 @@ t.test("it works with @libsql/client: in-memory", async (t) => {
174206
}
175207
});
176208

177-
t.test("it works with @libsql/client: http", async (t) => {
209+
t.test("it works with @libsql/client: http", testOpts, async (t) => {
178210
const { createClient } =
179211
require("@libsql/client") as typeof import("@libsql/client");
180212

@@ -270,6 +302,32 @@ t.test("it works with @libsql/client: http", async (t) => {
270302
"Zen has blocked an SQL injection: @libsql/client.batch(...) originating from body.myTitle"
271303
);
272304
}
305+
306+
const transaction = await client.transaction("write");
307+
308+
const error9 = await t.rejects(() =>
309+
transaction.execute("SELECT 1;-- should be blocked")
310+
);
311+
t.ok(error9 instanceof Error);
312+
if (error9 instanceof Error) {
313+
t.same(
314+
error9.message,
315+
"Zen has blocked an SQL injection: @libsql/client.transaction.execute(...) originating from body.myTitle"
316+
);
317+
}
318+
319+
const error10 = await t.rejects(() =>
320+
transaction.batch(["SELECT 1;-- should be blocked"])
321+
);
322+
t.ok(error10 instanceof Error);
323+
if (error10 instanceof Error) {
324+
t.same(
325+
error10.message,
326+
"Zen has blocked an SQL injection: @libsql/client.transaction.batch(...) originating from body.myTitle"
327+
);
328+
}
329+
330+
await transaction.commit();
273331
});
274332
} catch (error: any) {
275333
t.fail(error);

library/sinks/LibSQLClient.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,11 @@ export class LibSQLClient implements Wrapper {
7070

7171
wrap(hooks: Hooks) {
7272
const sqlFunctions = ["execute", "executeMultiple", "batch", "migrate"];
73-
74-
// Todo transactions
73+
const transactionSqlFunctions = ["execute", "executeMultiple", "batch"];
7574

7675
hooks
7776
.addPackage("@libsql/client")
78-
.withVersion("^0.14.0")
77+
.withVersion("^0.10.0")
7978
.onRequire((exports, pkgInfo) => {
8079
// Modify the return value of createClient function -> the client object
8180
wrapExport(exports, "createClient", pkgInfo, {
@@ -88,6 +87,28 @@ export class LibSQLClient implements Wrapper {
8887
},
8988
});
9089
}
90+
91+
// Wrap transaction functions
92+
wrapExport(returnValue, "transaction", pkgInfo, {
93+
modifyReturnValue: async (args, returnValue, agent) => {
94+
// Await the promise
95+
const transaction = await returnValue;
96+
97+
// Wrap all functions in the transaction object
98+
for (const transactionFunc of transactionSqlFunctions) {
99+
wrapExport(transaction, transactionFunc, pkgInfo, {
100+
inspectArgs: (args) => {
101+
return this.inspectQuery(
102+
`@libsql/client.transaction.${transactionFunc}`,
103+
args
104+
);
105+
},
106+
});
107+
}
108+
109+
return transaction;
110+
},
111+
});
91112
return returnValue;
92113
},
93114
});

0 commit comments

Comments
 (0)