@saasqlite/client 0.0.1

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/dist/index.js ADDED
@@ -0,0 +1,861 @@
1
+ import { createConnection } from "node:net";
2
+ //#region src/errors.ts
3
+ var SaaSQLiteError = class extends Error {
4
+ code;
5
+ details;
6
+ queryId;
7
+ sqliteCode;
8
+ cause;
9
+ constructor(code, message, options) {
10
+ super(message);
11
+ this.name = "SaaSQLiteError";
12
+ this.code = code;
13
+ this.details = options?.details;
14
+ this.queryId = options?.queryId;
15
+ this.sqliteCode = options?.sqliteCode || code;
16
+ this.cause = options?.cause;
17
+ }
18
+ toJSON() {
19
+ return {
20
+ code: this.code,
21
+ message: this.message,
22
+ details: this.details,
23
+ queryId: this.queryId,
24
+ sqliteCode: this.sqliteCode
25
+ };
26
+ }
27
+ };
28
+ //#endregion
29
+ //#region src/params.ts
30
+ function resolveParams(sql, params) {
31
+ if (!params) return {
32
+ sql,
33
+ positionalParams: []
34
+ };
35
+ if (Array.isArray(params)) return {
36
+ sql,
37
+ positionalParams: params
38
+ };
39
+ const paramObj = params;
40
+ const values = /* @__PURE__ */ new Map();
41
+ const positionalParams = [];
42
+ for (const [key, value] of Object.entries(paramObj)) {
43
+ const normalized = key.replace(/^[:@$]/, "");
44
+ values.set(normalized.toLowerCase(), value);
45
+ }
46
+ let result = sql;
47
+ result = result.replace(/@([A-Za-z_]\w*)/g, (_, name) => {
48
+ const val = values.get(name.toLowerCase());
49
+ if (val === void 0) throw new SaaSQLiteError("SQLITE_RANGE", `No value supplied for param binding @${name}`);
50
+ positionalParams.push(val);
51
+ return "?";
52
+ });
53
+ result = result.replace(/:(?![.0-9])([A-Za-z_]\w*)/g, (_, name) => {
54
+ const val = values.get(name.toLowerCase());
55
+ if (val === void 0) throw new SaaSQLiteError("SQLITE_RANGE", `No value supplied for param binding :${name}`);
56
+ positionalParams.push(val);
57
+ return "?";
58
+ });
59
+ result = result.replace(/\$([A-Za-z_]\w*)/g, (_, name) => {
60
+ const val = values.get(name.toLowerCase());
61
+ if (val === void 0) throw new SaaSQLiteError("SQLITE_RANGE", `No value supplied for param binding $${name}`);
62
+ positionalParams.push(val);
63
+ return "?";
64
+ });
65
+ return {
66
+ sql: result,
67
+ positionalParams
68
+ };
69
+ }
70
+ function countParameters(sql) {
71
+ let count = 0;
72
+ let i = 0;
73
+ while (i < sql.length) {
74
+ const ch = sql[i];
75
+ if (ch === "?") {
76
+ if (i + 1 < sql.length && /\d/.test(sql[i + 1])) {
77
+ i += 2;
78
+ continue;
79
+ }
80
+ count++;
81
+ i++;
82
+ continue;
83
+ }
84
+ if (ch === "@") {
85
+ if (i + 1 < sql.length && /[A-Za-z_]/.test(sql[i + 1])) {
86
+ i += 2;
87
+ while (i < sql.length && /\w/.test(sql[i])) i++;
88
+ continue;
89
+ }
90
+ i++;
91
+ continue;
92
+ }
93
+ if (ch === ":" && i + 1 < sql.length && /[A-Za-z_]/.test(sql[i + 1])) {
94
+ i += 2;
95
+ while (i < sql.length && /\w/.test(sql[i])) i++;
96
+ continue;
97
+ }
98
+ if (ch === "$" && i + 1 < sql.length && /[A-Za-z_]/.test(sql[i + 1])) {
99
+ i += 2;
100
+ while (i < sql.length && /\w/.test(sql[i])) i++;
101
+ continue;
102
+ }
103
+ i++;
104
+ }
105
+ return count;
106
+ }
107
+ //#endregion
108
+ //#region src/statement.ts
109
+ function isReaderSQL(sql) {
110
+ const trimmed = sql.trim().toUpperCase();
111
+ return trimmed.startsWith("SELECT") || trimmed.startsWith("PRAGMA") || trimmed.startsWith("WITH");
112
+ }
113
+ function isReadonlySQL(sql) {
114
+ const trimmed = sql.trim().toUpperCase();
115
+ if (trimmed.startsWith("SELECT")) return true;
116
+ if (trimmed.startsWith("PRAGMA")) return !trimmed.includes("=") && !trimmed.includes("CHECKPOINT");
117
+ return false;
118
+ }
119
+ function applyRowMode(row, columns, mode) {
120
+ switch (mode) {
121
+ case "pluck": return row[columns[0]];
122
+ case "raw": return columns.map((c) => row[c]);
123
+ case "expand":
124
+ const expanded = {};
125
+ for (const col of columns) {
126
+ const parts = col.includes(".") ? col.split(".") : [null, col];
127
+ const table = parts[0];
128
+ const name = parts[1];
129
+ expanded[table ? `${table}.${name}` : `$${name}`] = row[col];
130
+ }
131
+ return expanded;
132
+ default: return row;
133
+ }
134
+ }
135
+ var Statement = class {
136
+ source;
137
+ reader;
138
+ readonly;
139
+ _busy = false;
140
+ _changes = 0;
141
+ _pluck = false;
142
+ _raw = false;
143
+ _expand = false;
144
+ _boundParams = null;
145
+ constructor(sql, transport, timeoutMs) {
146
+ this.sql = sql;
147
+ this.transport = transport;
148
+ this.timeoutMs = timeoutMs;
149
+ this.source = sql;
150
+ this.reader = isReaderSQL(sql);
151
+ this.readonly = isReadonlySQL(sql);
152
+ }
153
+ get busy() {
154
+ return this._busy;
155
+ }
156
+ get changes() {
157
+ return this._changes;
158
+ }
159
+ pluck(enable = true) {
160
+ this._pluck = enable;
161
+ this._raw = false;
162
+ this._expand = false;
163
+ return this;
164
+ }
165
+ raw(enable = true) {
166
+ this._raw = enable;
167
+ this._pluck = false;
168
+ this._expand = false;
169
+ return this;
170
+ }
171
+ expand(enable = true) {
172
+ this._expand = enable;
173
+ this._pluck = false;
174
+ this._raw = false;
175
+ return this;
176
+ }
177
+ get rowMode() {
178
+ if (this._pluck) return "pluck";
179
+ if (this._raw) return "raw";
180
+ if (this._expand) return "expand";
181
+ return "normal";
182
+ }
183
+ bind(...params) {
184
+ if (this._boundParams !== null) throw new SaaSQLiteError("SQLITE_MISUSE", "Cannot bind parameters to an already bound statement");
185
+ this._boundParams = params;
186
+ return this;
187
+ }
188
+ columns() {
189
+ if (!this.reader) throw new SaaSQLiteError("SQLITE_MISUSE", "Cannot call columns() on a statement that does not return data");
190
+ return [];
191
+ }
192
+ async get(...params) {
193
+ const rows = await this.all(...params);
194
+ return rows.length > 0 ? rows[0] : void 0;
195
+ }
196
+ async all(...params) {
197
+ const { sql, positionalParams } = this.resolveParams(params);
198
+ const result = await this.executeRequest({
199
+ sql,
200
+ params: positionalParams
201
+ });
202
+ if (result.resultType === "select") {
203
+ const mode = this.rowMode;
204
+ if (mode === "normal") return result.rows;
205
+ return result.rows.map((row) => applyRowMode(row, result.columns, mode));
206
+ }
207
+ return [];
208
+ }
209
+ async run(...params) {
210
+ const { sql, positionalParams } = this.resolveParams(params);
211
+ const result = await this.executeRequest({
212
+ sql,
213
+ params: positionalParams
214
+ });
215
+ if (result.resultType === "run") {
216
+ this._changes = result.rowsAffected;
217
+ return {
218
+ changes: result.rowsAffected,
219
+ lastInsertRowid: result.lastInsertRowid || null
220
+ };
221
+ }
222
+ return {
223
+ changes: 0,
224
+ lastInsertRowid: null
225
+ };
226
+ }
227
+ async *iterate(...params) {
228
+ const rows = await this.all(...params);
229
+ for (const row of rows) yield row;
230
+ }
231
+ resolveParams(params) {
232
+ if (this._boundParams !== null) {
233
+ if (params.length > 0) throw new SaaSQLiteError("SQLITE_MISUSE", "Cannot supply parameters to a bound statement");
234
+ return {
235
+ sql: this.sql,
236
+ positionalParams: this._boundParams
237
+ };
238
+ }
239
+ if (params.length === 1 && !Array.isArray(params[0]) && typeof params[0] === "object" && params[0] !== null) {
240
+ const resolved = resolveParams(this.sql, params[0]);
241
+ return {
242
+ sql: resolved.sql,
243
+ positionalParams: resolved.positionalParams
244
+ };
245
+ }
246
+ return {
247
+ sql: this.sql,
248
+ positionalParams: params
249
+ };
250
+ }
251
+ async executeRequest(statement) {
252
+ this._busy = true;
253
+ try {
254
+ return await this.transport.query(statement.sql, statement.params ?? []);
255
+ } catch (error) {
256
+ if (error instanceof SaaSQLiteError) throw error;
257
+ if (error instanceof Error && error.name === "AbortError") throw new SaaSQLiteError("SQLITE_BUSY", `Query timed out after ${this.timeoutMs}ms`);
258
+ throw new SaaSQLiteError("SQLITE_ERROR", error instanceof Error ? error.message : "Unknown error occurred");
259
+ } finally {
260
+ this._busy = false;
261
+ }
262
+ }
263
+ };
264
+ //#endregion
265
+ //#region src/transaction.ts
266
+ var Transaction = class {
267
+ constructor(db) {
268
+ this.db = db;
269
+ }
270
+ prepare(sql) {
271
+ return this.db.prepare(sql);
272
+ }
273
+ async exec(sql) {
274
+ await this.db.exec(sql);
275
+ return this;
276
+ }
277
+ async run(sql, ...params) {
278
+ return this.db.run(sql, params);
279
+ }
280
+ async get(sql, ...params) {
281
+ return this.db.get(sql, params);
282
+ }
283
+ async all(sql, ...params) {
284
+ return this.db.all(sql, params);
285
+ }
286
+ };
287
+ //#endregion
288
+ //#region src/transport.ts
289
+ var HttpTransport = class {
290
+ baseUrl;
291
+ token;
292
+ timeoutMs;
293
+ closed = false;
294
+ constructor(opts) {
295
+ this.baseUrl = opts.url.replace(/\/+$/, "");
296
+ this.token = opts.token;
297
+ this.timeoutMs = opts.timeoutMs ?? 5e3;
298
+ }
299
+ async request(path, body) {
300
+ if (this.closed) throw new SaaSQLiteError("SQLITE_MISUSE", "HttpTransport is closed.");
301
+ const url = `${this.baseUrl}${path}`;
302
+ const controller = new AbortController();
303
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
304
+ try {
305
+ const res = await fetch(url, {
306
+ method: "POST",
307
+ headers: {
308
+ "Content-Type": "application/json",
309
+ "Authorization": `Bearer ${this.token}`
310
+ },
311
+ body: JSON.stringify(body),
312
+ signal: controller.signal
313
+ });
314
+ const data = await res.json();
315
+ if (!res.ok) {
316
+ const err = data.error || {};
317
+ throw new SaaSQLiteError(err.code || "HTTP_ERROR", err.message || `HTTP ${res.status}: ${res.statusText}`);
318
+ }
319
+ return data;
320
+ } catch (error) {
321
+ if (error instanceof SaaSQLiteError) throw error;
322
+ if (error.name === "AbortError") throw new SaaSQLiteError("QUERY_TIMEOUT", `Request timed out after ${this.timeoutMs}ms`);
323
+ throw new SaaSQLiteError("CONNECTION_ERROR", error.message || "Failed to connect to gateway");
324
+ } finally {
325
+ clearTimeout(timer);
326
+ }
327
+ }
328
+ async query(sql, params) {
329
+ const response = await this.request("/v1/query", {
330
+ sql,
331
+ params
332
+ });
333
+ if ("columns" in response && response.columns !== void 0) return {
334
+ resultType: "select",
335
+ columns: response.columns,
336
+ rows: response.rows || []
337
+ };
338
+ return {
339
+ resultType: "run",
340
+ rowsAffected: response.rowsAffected ?? 0,
341
+ lastInsertRowid: response.lastInsertRowid ?? 0
342
+ };
343
+ }
344
+ async batch(statements) {
345
+ return (await this.request("/v1/batch", { statements: statements.map((s) => ({
346
+ sql: s.sql,
347
+ params: s.params || []
348
+ })) })).results;
349
+ }
350
+ async close() {
351
+ this.closed = true;
352
+ }
353
+ };
354
+ //#endregion
355
+ //#region src/protocol.ts
356
+ const CMD_QUERY = 0;
357
+ const CMD_BATCH = 1;
358
+ const CMD_HEALTH = 2;
359
+ const STATUS_OK = 0;
360
+ const STATUS_ERR = 1;
361
+ const RESULT_SELECT = 0;
362
+ const RESULT_RUN = 1;
363
+ const VAL_NULL = 0;
364
+ const VAL_INT64 = 1;
365
+ const VAL_FLOAT64 = 2;
366
+ const VAL_TEXT = 3;
367
+ const VAL_BLOB = 4;
368
+ var Reader = class {
369
+ buf = Buffer.alloc(0);
370
+ async read(s, n) {
371
+ while (this.buf.length < n) {
372
+ const chunk = await new Promise((res, rej) => {
373
+ s.once("error", rej);
374
+ s.once("close", () => rej(new SaaSQLiteError("CONNECTION_ERROR", "Socket closed during read")));
375
+ s.once("data", (d) => res(d));
376
+ });
377
+ this.buf = Buffer.concat([this.buf, chunk]);
378
+ }
379
+ const r = this.buf.slice(0, n);
380
+ this.buf = this.buf.slice(n);
381
+ return r;
382
+ }
383
+ };
384
+ function w32(b, o, v) {
385
+ b[o] = v & 255;
386
+ b[o + 1] = v >> 8 & 255;
387
+ b[o + 2] = v >> 16 & 255;
388
+ b[o + 3] = v >> 24 & 255;
389
+ }
390
+ function r32(b, o) {
391
+ return b[o] | b[o + 1] << 8 | b[o + 2] << 16 | b[o + 3] << 24;
392
+ }
393
+ function w64(b, o, v) {
394
+ let u = BigInt(v) & 18446744073709551615n;
395
+ for (let i = 0; i < 8; i++) {
396
+ b[o + i] = Number(u & 255n);
397
+ u >>= 8n;
398
+ }
399
+ }
400
+ function r64(b, o) {
401
+ let u = 0n;
402
+ for (let i = 7; i >= 0; i--) u = u << 8n | BigInt(b[o + i]);
403
+ if (u & 1n << 63n) u -= 1n << 64n;
404
+ return Number(u);
405
+ }
406
+ function wF64(b, o, v) {
407
+ const d = Buffer.alloc(8);
408
+ d.writeDoubleLE(v, 0);
409
+ d.copy(b, o, 0, 8);
410
+ }
411
+ function rF64(b, o) {
412
+ return b.slice(o, o + 8).readDoubleLE(0);
413
+ }
414
+ function encParam(p) {
415
+ if (p == null) return Buffer.from([0]);
416
+ if (typeof p === "boolean") {
417
+ const b = Buffer.alloc(13);
418
+ b[0] = 1;
419
+ w32(b, 1, 0);
420
+ w64(b, 5, p ? 1 : 0);
421
+ return b;
422
+ }
423
+ if (typeof p === "number") {
424
+ const b = Buffer.alloc(13);
425
+ if (Number.isInteger(p)) {
426
+ b[0] = 1;
427
+ w32(b, 1, 0);
428
+ w64(b, 5, p);
429
+ } else {
430
+ b[0] = 2;
431
+ w32(b, 1, 0);
432
+ wF64(b, 5, p);
433
+ }
434
+ return b;
435
+ }
436
+ if (typeof p === "string") {
437
+ const t = Buffer.from(p, "utf-8");
438
+ const b = Buffer.alloc(5 + t.length);
439
+ b[0] = 3;
440
+ w32(b, 1, t.length);
441
+ t.copy(b, 5);
442
+ return b;
443
+ }
444
+ if (p instanceof Uint8Array) {
445
+ const bl = Buffer.from(p);
446
+ const b = Buffer.alloc(5 + bl.length);
447
+ b[0] = 4;
448
+ w32(b, 1, bl.length);
449
+ bl.copy(b, 5);
450
+ return b;
451
+ }
452
+ throw new SaaSQLiteError("INVALID_PARAM", `Unsupported param type: ${typeof p}`);
453
+ }
454
+ function encQuery(sql, params) {
455
+ const sb = Buffer.from(sql, "utf-8");
456
+ const pb = params.map(encParam);
457
+ const pl = pb.reduce((s, b) => s + b.length, 0);
458
+ const b = Buffer.alloc(5 + sb.length + 4 + pl);
459
+ let o = 0;
460
+ b[o++] = 0;
461
+ w32(b, o, sb.length);
462
+ o += 4;
463
+ sb.copy(b, o);
464
+ o += sb.length;
465
+ w32(b, o, params.length);
466
+ o += 4;
467
+ for (const p of pb) {
468
+ p.copy(b, o);
469
+ o += p.length;
470
+ }
471
+ return b;
472
+ }
473
+ function encBatch(stmts) {
474
+ const sbs = stmts.map((stmt) => {
475
+ const sb = Buffer.from(stmt.sql, "utf-8");
476
+ const ps = stmt.params || [];
477
+ const pb = ps.map(encParam);
478
+ const pl = pb.reduce((s, b) => s + b.length, 0);
479
+ const b = Buffer.alloc(4 + sb.length + 4 + pl);
480
+ let o = 0;
481
+ w32(b, o, sb.length);
482
+ o += 4;
483
+ sb.copy(b, o);
484
+ o += sb.length;
485
+ w32(b, o, ps.length);
486
+ o += 4;
487
+ for (const p of pb) {
488
+ p.copy(b, o);
489
+ o += p.length;
490
+ }
491
+ return b;
492
+ });
493
+ const sl = sbs.reduce((s, b) => s + b.length, 0);
494
+ const b = Buffer.alloc(5 + sl);
495
+ let o = 0;
496
+ b[o++] = 1;
497
+ w32(b, o, stmts.length);
498
+ o += 4;
499
+ for (const s of sbs) {
500
+ s.copy(b, o);
501
+ o += s.length;
502
+ }
503
+ return b;
504
+ }
505
+ function encHealth() {
506
+ return Buffer.from([2]);
507
+ }
508
+ async function readVal(r, s) {
509
+ const vt = (await r.read(s, 1))[0];
510
+ if (vt === 0) return null;
511
+ if (vt === 1) {
512
+ await r.read(s, 4);
513
+ return r64(await r.read(s, 8), 0);
514
+ }
515
+ if (vt === 2) {
516
+ await r.read(s, 4);
517
+ return rF64(await r.read(s, 8), 0);
518
+ }
519
+ if (vt === 3) {
520
+ const l = r32(await r.read(s, 4), 0);
521
+ return (await r.read(s, l)).toString("utf-8");
522
+ }
523
+ if (vt === 4) {
524
+ const l = r32(await r.read(s, 4), 0);
525
+ return await r.read(s, l);
526
+ }
527
+ throw new SaaSQLiteError("PROTOCOL_ERROR", `Unknown value type: ${vt}`);
528
+ }
529
+ async function readSelect(r, s) {
530
+ const cc = r32(await r.read(s, 4), 0);
531
+ const cols = [];
532
+ for (let i = 0; i < cc; i++) {
533
+ const cl = r32(await r.read(s, 4), 0);
534
+ cols.push((await r.read(s, cl)).toString("utf-8"));
535
+ }
536
+ const rc = r32(await r.read(s, 4), 0);
537
+ const rows = [];
538
+ for (let i = 0; i < rc; i++) {
539
+ const row = {};
540
+ for (let j = 0; j < cc; j++) row[cols[j]] = await readVal(r, s);
541
+ rows.push(row);
542
+ }
543
+ return {
544
+ columns: cols,
545
+ rows
546
+ };
547
+ }
548
+ async function readRun(r, s) {
549
+ return {
550
+ rowsAffected: r32(await r.read(s, 4), 0),
551
+ lastInsertRowid: r64(await r.read(s, 8), 0)
552
+ };
553
+ }
554
+ async function checkStatus(r, s) {
555
+ if ((await r.read(s, 1))[0] === 1) {
556
+ const ml = r32(await r.read(s, 4), 0);
557
+ throw new SaaSQLiteError("SQLITE_ERROR", (await r.read(s, ml)).toString("utf-8"));
558
+ }
559
+ }
560
+ //#endregion
561
+ //#region src/socket-transport.ts
562
+ var SocketTransport = class {
563
+ sockPath;
564
+ password;
565
+ timeoutMs;
566
+ closed = false;
567
+ constructor(opts) {
568
+ this.sockPath = opts.sock;
569
+ this.password = opts.password;
570
+ this.timeoutMs = opts.timeoutMs ?? 5e3;
571
+ }
572
+ async withSocket(fn) {
573
+ if (this.closed) throw new SaaSQLiteError("SQLITE_MISUSE", "SocketTransport is closed.");
574
+ return new Promise((resolve, reject) => {
575
+ const sock = createConnection(this.sockPath);
576
+ let settled = false;
577
+ const timer = setTimeout(() => {
578
+ if (!settled) {
579
+ settled = true;
580
+ sock.destroy();
581
+ reject(new SaaSQLiteError("QUERY_TIMEOUT", `Timed out after ${this.timeoutMs}ms`));
582
+ }
583
+ }, this.timeoutMs);
584
+ sock.once("connect", () => {
585
+ sock.write(`Bearer ${this.password}\n`);
586
+ fn(sock, new Reader()).then((res) => {
587
+ if (!settled) {
588
+ settled = true;
589
+ clearTimeout(timer);
590
+ sock.destroy();
591
+ resolve(res);
592
+ }
593
+ }).catch((err) => {
594
+ if (!settled) {
595
+ settled = true;
596
+ clearTimeout(timer);
597
+ sock.destroy();
598
+ reject(err);
599
+ }
600
+ });
601
+ });
602
+ sock.once("error", (err) => {
603
+ if (!settled) {
604
+ settled = true;
605
+ clearTimeout(timer);
606
+ reject(new SaaSQLiteError("CONNECTION_ERROR", err.message));
607
+ }
608
+ });
609
+ });
610
+ }
611
+ async query(sql, params) {
612
+ return this.withSocket(async (s, r) => {
613
+ s.write(encQuery(sql, params));
614
+ await checkStatus(r, s);
615
+ if ((await r.read(s, 1))[0] === 0) {
616
+ const sel = await readSelect(r, s);
617
+ return {
618
+ resultType: "select",
619
+ columns: sel.columns,
620
+ rows: sel.rows
621
+ };
622
+ } else {
623
+ const run = await readRun(r, s);
624
+ return {
625
+ resultType: "run",
626
+ rowsAffected: run.rowsAffected,
627
+ lastInsertRowid: run.lastInsertRowid
628
+ };
629
+ }
630
+ });
631
+ }
632
+ async batch(statements) {
633
+ return this.withSocket(async (s, r) => {
634
+ s.write(encBatch(statements));
635
+ await checkStatus(r, s);
636
+ await r.read(s, 1);
637
+ const count = r32(await r.read(s, 4), 0);
638
+ const results = [];
639
+ for (let i = 0; i < count; i++) {
640
+ if ((await r.read(s, 1))[0] === 1) {
641
+ const ml = r32(await r.read(s, 4), 0);
642
+ const msg = (await r.read(s, ml)).toString("utf-8");
643
+ results.push({
644
+ code: "SQLITE_ERROR",
645
+ message: msg
646
+ });
647
+ continue;
648
+ }
649
+ if ((await r.read(s, 1))[0] === 0) {
650
+ const sel = await readSelect(r, s);
651
+ results.push({
652
+ type: "select",
653
+ result: {
654
+ columns: sel.columns,
655
+ rows: sel.rows
656
+ }
657
+ });
658
+ } else {
659
+ const rn = await readRun(r, s);
660
+ results.push({
661
+ type: "run",
662
+ result: {
663
+ rowsAffected: rn.rowsAffected,
664
+ lastInsertRowid: rn.lastInsertRowid,
665
+ durationMs: 0
666
+ }
667
+ });
668
+ }
669
+ }
670
+ return results;
671
+ });
672
+ }
673
+ async close() {
674
+ this.closed = true;
675
+ }
676
+ };
677
+ //#endregion
678
+ //#region src/client.ts
679
+ /**
680
+ * Default SaaSQLite gateway (remote cloud endpoint).
681
+ */
682
+ const DEFAULT_GATEWAY_URL = "https://api.saasqlite.com";
683
+ function isHttpUrl(value) {
684
+ return /^https?:\/\//i.test(value);
685
+ }
686
+ function envGatewayUrl() {
687
+ if (typeof process === "undefined" || !process.env) return void 0;
688
+ const v = process.env.SAASQLITE_GATEWAY_URL;
689
+ return v && v.trim() ? v : void 0;
690
+ }
691
+ /**
692
+ * Resolve the gateway base URL for a public-API constructor call.
693
+ * Precedence: full http(s) URL passed as databaseId > options.gatewayUrl
694
+ * > $SAASQLITE_GATEWAY_URL > DEFAULT_GATEWAY_URL.
695
+ */
696
+ function resolveGatewayUrl(databaseId, opts = {}) {
697
+ if (isHttpUrl(databaseId)) return databaseId;
698
+ if (opts.gatewayUrl && opts.gatewayUrl.trim()) return opts.gatewayUrl;
699
+ return envGatewayUrl() ?? "https://api.saasqlite.com";
700
+ }
701
+ var SaaSQLite = class {
702
+ _transport;
703
+ _timeoutMs;
704
+ _closed = false;
705
+ _txDepth = 0;
706
+ get open() {
707
+ return !this._closed;
708
+ }
709
+ name;
710
+ constructor(first, second, third) {
711
+ if (typeof first === "string") {
712
+ const apiKey = typeof second === "string" ? second : "";
713
+ const opts = third ?? {};
714
+ const timeoutMs = opts.timeoutMs ?? 5e3;
715
+ this._timeoutMs = timeoutMs;
716
+ if (!isHttpUrl(first) && !first.trim()) throw new SaaSQLiteError("SQLITE_MISUSE", "databaseId must be a non-empty string (or a full http(s) gateway URL)");
717
+ this._transport = new HttpTransport({
718
+ url: resolveGatewayUrl(first, opts),
719
+ token: apiKey,
720
+ timeoutMs: this._timeoutMs
721
+ });
722
+ this.name = first;
723
+ } else {
724
+ this._timeoutMs = first.timeoutMs ?? 5e3;
725
+ if ("sock" in first) {
726
+ this._transport = new SocketTransport({
727
+ sock: first.sock,
728
+ password: first.password,
729
+ timeoutMs: this._timeoutMs
730
+ });
731
+ this.name = first.sock;
732
+ } else {
733
+ this._transport = new HttpTransport({
734
+ url: first.url,
735
+ token: first.token,
736
+ timeoutMs: this._timeoutMs
737
+ });
738
+ this.name = first.url;
739
+ }
740
+ }
741
+ }
742
+ async exec(sql) {
743
+ this.assertOpen();
744
+ try {
745
+ await this._transport.query(sql, []);
746
+ } catch (error) {
747
+ if (error instanceof SaaSQLiteError) throw error;
748
+ throw new SaaSQLiteError("SQLITE_ERROR", error instanceof Error ? error.message : "Unknown error occurred");
749
+ }
750
+ return this;
751
+ }
752
+ prepare(sql) {
753
+ this.assertOpen();
754
+ if (!sql || !sql.trim()) throw new SaaSQLiteError("SQLITE_MISUSE", "Cannot prepare empty SQL");
755
+ return new Statement(sql, this._transport, this._timeoutMs);
756
+ }
757
+ async pragma(sql, options) {
758
+ this.assertOpen();
759
+ if (typeof sql !== "string") throw new SaaSQLiteError("SQLITE_MISUSE", "pragma() requires a string argument");
760
+ if (sql.toUpperCase().startsWith("PRAGMA")) throw new SaaSQLiteError("SQLITE_ERROR", "PRAGMA keyword should not be included in pragma() call");
761
+ const pragmaSql = `PRAGMA ${sql}`;
762
+ const result = await this._transport.query(pragmaSql, []);
763
+ if (result.resultType !== "select") return [];
764
+ if (options?.simple) return result.rows.length > 0 ? Object.values(result.rows[0])[0] : void 0;
765
+ return result.rows;
766
+ }
767
+ async transaction(fn) {
768
+ this.assertOpen();
769
+ if (typeof fn !== "function") throw new SaaSQLiteError("SQLITE_MISUSE", "transaction() requires a function argument");
770
+ const isNested = this._txDepth > 0;
771
+ const savepointName = `sp_${this._txDepth}`;
772
+ this._txDepth++;
773
+ try {
774
+ if (isNested) await this.exec(`SAVEPOINT ${savepointName}`);
775
+ else await this.exec("BEGIN IMMEDIATE");
776
+ const result = await fn(new Transaction(this));
777
+ if (isNested) await this.exec(`RELEASE SAVEPOINT ${savepointName}`);
778
+ else await this.exec("COMMIT");
779
+ return result;
780
+ } catch (err) {
781
+ if (isNested) await this.exec(`ROLLBACK TO SAVEPOINT ${savepointName}`).catch(() => {});
782
+ else await this.exec("ROLLBACK").catch(() => {});
783
+ throw err;
784
+ } finally {
785
+ this._txDepth--;
786
+ }
787
+ }
788
+ async query(sql, params) {
789
+ this.assertOpen();
790
+ const stmt = this.prepare(sql);
791
+ if (!params) return stmt.all();
792
+ return Array.isArray(params) ? stmt.all(...params) : stmt.all(params);
793
+ }
794
+ async get(sql, params) {
795
+ this.assertOpen();
796
+ const stmt = this.prepare(sql);
797
+ if (!params) return stmt.get();
798
+ return Array.isArray(params) ? stmt.get(...params) : stmt.get(params);
799
+ }
800
+ async all(sql, params) {
801
+ this.assertOpen();
802
+ const stmt = this.prepare(sql);
803
+ if (!params) return stmt.all();
804
+ return Array.isArray(params) ? stmt.all(...params) : stmt.all(params);
805
+ }
806
+ async run(sql, params) {
807
+ this.assertOpen();
808
+ const stmt = this.prepare(sql);
809
+ if (!params) return stmt.run();
810
+ return Array.isArray(params) ? stmt.run(...params) : stmt.run(params);
811
+ }
812
+ async batch(statements) {
813
+ this.assertOpen();
814
+ try {
815
+ return await this._transport.batch(statements);
816
+ } catch (error) {
817
+ if (error instanceof SaaSQLiteError) throw error;
818
+ throw new SaaSQLiteError("SQLITE_ERROR", error instanceof Error ? error.message : "Unknown error occurred");
819
+ }
820
+ }
821
+ async close() {
822
+ this._closed = true;
823
+ await this._transport.close();
824
+ }
825
+ function(_name, ..._args) {
826
+ throw new SaaSQLiteError("NOT_SUPPORTED", "User-defined functions are not supported in SaaSQLite");
827
+ }
828
+ aggregate(_name, ..._args) {
829
+ throw new SaaSQLiteError("NOT_SUPPORTED", "Custom aggregates are not supported in SaaSQLite");
830
+ }
831
+ table(_name, ..._args) {
832
+ throw new SaaSQLiteError("NOT_SUPPORTED", "Virtual tables are not supported in SaaSQLite");
833
+ }
834
+ loadExtension(_path) {
835
+ throw new SaaSQLiteError("NOT_SUPPORTED", "Loading extensions is not supported in SaaSQLite");
836
+ }
837
+ backup(_destination) {
838
+ throw new SaaSQLiteError("NOT_SUPPORTED", "Backup is not supported via SDK. Use the dashboard or API.");
839
+ }
840
+ serialize() {
841
+ throw new SaaSQLiteError("NOT_SUPPORTED", "Serialize is not supported in SaaSQLite");
842
+ }
843
+ checkpoint() {
844
+ throw new SaaSQLiteError("NOT_SUPPORTED", "Checkpoint is not supported via SDK");
845
+ }
846
+ defaultSafeIntegers(_enabled) {
847
+ throw new SaaSQLiteError("NOT_SUPPORTED", "defaultSafeIntegers is not supported in SaaSQLite");
848
+ }
849
+ unsafeMode(_enabled) {
850
+ throw new SaaSQLiteError("NOT_SUPPORTED", "unsafeMode is not supported in SaaSQLite");
851
+ }
852
+ verbose(_fn) {
853
+ throw new SaaSQLiteError("NOT_SUPPORTED", "verbose is not supported in SaaSQLite");
854
+ }
855
+ assertOpen() {
856
+ if (this._closed) throw new SaaSQLiteError("SQLITE_MISUSE", "SaaSQLite connection is closed");
857
+ }
858
+ };
859
+ SaaSQLite.SqliteError = SaaSQLiteError;
860
+ //#endregion
861
+ export { CMD_BATCH, CMD_HEALTH, CMD_QUERY, DEFAULT_GATEWAY_URL, HttpTransport, RESULT_RUN, RESULT_SELECT, Reader, STATUS_ERR, STATUS_OK, SaaSQLite, SaaSQLiteError, SocketTransport, Statement, Transaction, VAL_BLOB, VAL_FLOAT64, VAL_INT64, VAL_NULL, VAL_TEXT, checkStatus, countParameters, encBatch, encHealth, encParam, encQuery, r32, r64, rF64, readRun, readSelect, readVal, resolveGatewayUrl, resolveParams, w32, w64, wF64 };