@persql/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @persql/sdk
2
+
3
+ TypeScript SDK for [PerSQL](https://persql.com) — SQLite databases on the edge, designed for AI agents.
4
+
5
+ ```bash
6
+ npm i @persql/sdk
7
+ ```
8
+
9
+ ## Quick start
10
+
11
+ ```ts
12
+ import { PerSQL } from "@persql/sdk";
13
+
14
+ const persql = new PerSQL({ token: process.env.PERSQL_TOKEN! });
15
+ const db = persql.database("acme/orders");
16
+
17
+ // Query — returns rows reshaped as objects.
18
+ const { data } = await db.query<{ id: number; email: string }>(
19
+ "SELECT id, email FROM customers WHERE id = ?",
20
+ [42]
21
+ );
22
+
23
+ // Batch — multiple statements in one round-trip.
24
+ const results = await db.batch([
25
+ { sql: "INSERT INTO orders (id, total) VALUES (?, ?)", params: ["o-1", 99.5] },
26
+ { sql: "UPDATE customers SET last_order = ? WHERE id = ?", params: ["o-1", "c-1"] },
27
+ ]);
28
+
29
+ // Transaction — same as batch with rollback on error.
30
+ await db.transaction([
31
+ { sql: "INSERT INTO accounts (id, balance) VALUES (?, ?)", params: ["a-1", 100] },
32
+ { sql: "INSERT INTO accounts (id, balance) VALUES (?, ?)", params: ["a-2", 0] },
33
+ ]);
34
+ ```
35
+
36
+ ## Use as a tool with Anthropic / OpenAI
37
+
38
+ ```ts
39
+ import Anthropic from "@anthropic-ai/sdk";
40
+ import { PerSQL } from "@persql/sdk";
41
+
42
+ const persql = new PerSQL({ token: process.env.PERSQL_TOKEN! });
43
+ const db = persql.database("acme/orders");
44
+ const tool = db.asTool().anthropic;
45
+
46
+ const anthropic = new Anthropic();
47
+ const response = await anthropic.messages.create({
48
+ model: "claude-opus-4-7",
49
+ max_tokens: 1024,
50
+ tools: [tool],
51
+ messages: [{ role: "user", content: "How many orders did we ship last week?" }],
52
+ });
53
+
54
+ // When the model calls the tool, run it and feed the result back:
55
+ for (const block of response.content) {
56
+ if (block.type === "tool_use" && block.name === tool.name) {
57
+ const result = await db.runTool(block.input as { sql: string; params?: unknown[] });
58
+ // …pass `result` back to the model in the next turn.
59
+ }
60
+ }
61
+ ```
62
+
63
+ ## Use with Drizzle
64
+
65
+ ```bash
66
+ npm i drizzle-orm
67
+ ```
68
+
69
+ ```ts
70
+ import { drizzle } from "drizzle-orm/sqlite-proxy";
71
+ import { PerSQL } from "@persql/sdk";
72
+ import * as schema from "./db/schema";
73
+
74
+ const persql = new PerSQL({ token: process.env.PERSQL_TOKEN! });
75
+ const db = drizzle(persql.database("acme/orders").driver(), { schema });
76
+
77
+ const recent = await db.select().from(schema.orders).limit(10);
78
+ ```
79
+
80
+ `db.driver()` returns the callback shape `drizzle-orm/sqlite-proxy`
81
+ expects. Generate the schema file once with the bundled
82
+ `persql-codegen` bin:
83
+
84
+ ```bash
85
+ PERSQL_TOKEN=psql_live_… npx persql-codegen --db acme/orders --out src/db/schema.ts
86
+ ```
87
+
88
+ ## Local mode (tests)
89
+
90
+ Run the SDK against a local SQLite file (or `:memory:`) so tests
91
+ don't hit your live database:
92
+
93
+ ```bash
94
+ npm i -D better-sqlite3
95
+ ```
96
+
97
+ ```ts
98
+ import { PerSQL } from "@persql/sdk";
99
+
100
+ const persql = new PerSQL({ local: ":memory:" }); // or "./test.db"
101
+ const db = persql.database("test/db");
102
+
103
+ await db.query("CREATE TABLE customers (id INTEGER, email TEXT)");
104
+ await db.query("INSERT INTO customers VALUES (?, ?)", [1, "a@b.co"]);
105
+ const { data } = await db.query("SELECT * FROM customers");
106
+ ```
107
+
108
+ `query / batch / transaction / tables / explain / schema` route
109
+ through `better-sqlite3` (lazy-loaded — never required for live use).
110
+ `vectors`, `blob`, and `subscribe` throw — they have no local
111
+ equivalent. The same `db.driver()` works in local mode, so a
112
+ Drizzle-based data layer needs zero changes between tests and prod.
113
+
114
+ Call `persql.close()` to release the file handle when done.
115
+
116
+ ## Idempotency
117
+
118
+ Pass an `Idempotency-Key` to make writes safely retryable:
119
+
120
+ ```ts
121
+ await db.query("INSERT INTO events (id, name) VALUES (?, ?)", ["e-1", "click"], {
122
+ idempotencyKey: "evt-e-1-2026-04-29",
123
+ });
124
+ ```
125
+
126
+ Cached for 24 hours. A retry with the same key returns the cached response.
127
+
128
+ ## Errors
129
+
130
+ ```ts
131
+ import { PerSQLError, RateLimitError } from "@persql/sdk";
132
+
133
+ try {
134
+ await db.query("SELECT 1");
135
+ } catch (e) {
136
+ if (e instanceof RateLimitError) {
137
+ console.log(`Slow down for ${e.retryAfterSeconds}s`);
138
+ } else if (e instanceof PerSQLError) {
139
+ console.log(`API error ${e.status}: ${e.message}`);
140
+ }
141
+ }
142
+ ```
143
+
144
+ ## License
145
+
146
+ MIT