@tursodatabase/serverless 1.2.0-pre.2 → 1.2.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.
@@ -1,966 +1 @@
1
- // src/error.ts
2
- var DatabaseError = class _DatabaseError extends Error {
3
- constructor(message, code, rawCode, cause) {
4
- super(message);
5
- this.name = "DatabaseError";
6
- this.code = code;
7
- this.rawCode = rawCode;
8
- this.cause = cause;
9
- Object.setPrototypeOf(this, _DatabaseError.prototype);
10
- }
11
- };
12
- var TimeoutError = class _TimeoutError extends DatabaseError {
13
- constructor(message = "Query timed out", cause) {
14
- super(message, "TIMEOUT", void 0, cause);
15
- this.name = "TimeoutError";
16
- Object.setPrototypeOf(this, _TimeoutError.prototype);
17
- }
18
- };
19
-
20
- // src/protocol.ts
21
- function encodeValue(value) {
22
- if (value === null || value === void 0) {
23
- return { type: "null" };
24
- }
25
- if (typeof value === "number") {
26
- if (!Number.isFinite(value)) {
27
- throw new Error("Only finite numbers (not Infinity or NaN) can be passed as arguments");
28
- }
29
- if (Number.isSafeInteger(value)) {
30
- return { type: "integer", value: value.toString() };
31
- }
32
- return { type: "float", value };
33
- }
34
- if (typeof value === "bigint") {
35
- return { type: "integer", value: value.toString() };
36
- }
37
- if (typeof value === "boolean") {
38
- return { type: "integer", value: value ? "1" : "0" };
39
- }
40
- if (typeof value === "string") {
41
- return { type: "text", value };
42
- }
43
- if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
44
- const base64 = btoa(String.fromCharCode(...new Uint8Array(value)));
45
- return { type: "blob", base64 };
46
- }
47
- return { type: "text", value: String(value) };
48
- }
49
- function decodeValue(value, safeIntegers = false) {
50
- switch (value.type) {
51
- case "null":
52
- return null;
53
- case "integer":
54
- if (safeIntegers) {
55
- return BigInt(value.value);
56
- }
57
- return parseInt(value.value, 10);
58
- case "float":
59
- return value.value;
60
- case "text":
61
- return value.value;
62
- case "blob":
63
- if (value.base64 !== void 0 && value.base64 !== null) {
64
- let b64 = value.base64;
65
- while (b64.length % 4 !== 0) {
66
- b64 += "=";
67
- }
68
- const binaryString = atob(b64);
69
- const bytes = new Uint8Array(binaryString.length);
70
- for (let i = 0; i < binaryString.length; i++) {
71
- bytes[i] = binaryString.charCodeAt(i);
72
- }
73
- return Buffer.from(bytes);
74
- }
75
- return Buffer.alloc(0);
76
- default:
77
- return null;
78
- }
79
- }
80
- var ENCRYPTION_KEY_HEADER = "x-turso-encryption-key";
81
- function wrapAbortError(error) {
82
- if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) {
83
- throw new TimeoutError("Query timed out");
84
- }
85
- throw error;
86
- }
87
- async function executeCursor(url, authToken, request, remoteEncryptionKey, signal) {
88
- const headers = {
89
- "Content-Type": "application/json"
90
- };
91
- if (authToken) {
92
- headers["Authorization"] = `Bearer ${authToken}`;
93
- }
94
- if (remoteEncryptionKey) {
95
- headers[ENCRYPTION_KEY_HEADER] = remoteEncryptionKey;
96
- }
97
- let response;
98
- try {
99
- response = await fetch(`${url}/v3/cursor`, {
100
- method: "POST",
101
- headers,
102
- body: JSON.stringify(request),
103
- signal
104
- });
105
- } catch (error) {
106
- wrapAbortError(error);
107
- }
108
- if (!response.ok) {
109
- let errorMessage = `HTTP error! status: ${response.status}`;
110
- try {
111
- const errorBody = await response.text();
112
- const errorData = JSON.parse(errorBody);
113
- if (errorData.message) {
114
- errorMessage = errorData.message;
115
- }
116
- } catch {
117
- }
118
- throw new DatabaseError(errorMessage);
119
- }
120
- const reader = response.body?.getReader();
121
- if (!reader) {
122
- throw new DatabaseError("No response body");
123
- }
124
- const decoder = new TextDecoder();
125
- let buffer = "";
126
- let cursorResponse;
127
- try {
128
- while (!cursorResponse) {
129
- const { done, value } = await reader.read();
130
- if (done) break;
131
- buffer += decoder.decode(value, { stream: true });
132
- const newlineIndex = buffer.indexOf("\n");
133
- if (newlineIndex !== -1) {
134
- const line = buffer.slice(0, newlineIndex).trim();
135
- buffer = buffer.slice(newlineIndex + 1);
136
- if (line) {
137
- cursorResponse = JSON.parse(line);
138
- break;
139
- }
140
- }
141
- }
142
- } catch (error) {
143
- reader.releaseLock();
144
- wrapAbortError(error);
145
- }
146
- if (!cursorResponse) {
147
- reader.releaseLock();
148
- throw new DatabaseError("No cursor response received");
149
- }
150
- async function* parseEntries() {
151
- try {
152
- let newlineIndex;
153
- while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
154
- const line = buffer.slice(0, newlineIndex).trim();
155
- buffer = buffer.slice(newlineIndex + 1);
156
- if (line) {
157
- yield JSON.parse(line);
158
- }
159
- }
160
- while (true) {
161
- let readResult;
162
- try {
163
- readResult = await reader.read();
164
- } catch (error) {
165
- wrapAbortError(error);
166
- }
167
- if (readResult.done) break;
168
- buffer += decoder.decode(readResult.value, { stream: true });
169
- while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
170
- const line = buffer.slice(0, newlineIndex).trim();
171
- buffer = buffer.slice(newlineIndex + 1);
172
- if (line) {
173
- yield JSON.parse(line);
174
- }
175
- }
176
- }
177
- if (buffer.trim()) {
178
- yield JSON.parse(buffer.trim());
179
- }
180
- } finally {
181
- reader.releaseLock();
182
- }
183
- }
184
- return { response: cursorResponse, entries: parseEntries() };
185
- }
186
- async function executePipeline(url, authToken, request, remoteEncryptionKey, signal) {
187
- const headers = {
188
- "Content-Type": "application/json"
189
- };
190
- if (authToken) {
191
- headers["Authorization"] = `Bearer ${authToken}`;
192
- }
193
- if (remoteEncryptionKey) {
194
- headers[ENCRYPTION_KEY_HEADER] = remoteEncryptionKey;
195
- }
196
- let response;
197
- try {
198
- response = await fetch(`${url}/v3/pipeline`, {
199
- method: "POST",
200
- headers,
201
- body: JSON.stringify(request),
202
- signal
203
- });
204
- } catch (error) {
205
- wrapAbortError(error);
206
- }
207
- if (!response.ok) {
208
- throw new DatabaseError(`HTTP error! status: ${response.status}`);
209
- }
210
- return response.json();
211
- }
212
-
213
- // src/args.ts
214
- function encodeSqlArgs(args = []) {
215
- let positionalArgs = [];
216
- let namedArgs = [];
217
- if (Array.isArray(args)) {
218
- positionalArgs = args.map(encodeValue);
219
- } else {
220
- const keys = Object.keys(args);
221
- const isNumericKeys = keys.length > 0 && keys.every((key) => /^\d+$/.test(key));
222
- if (isNumericKeys) {
223
- const sortedKeys = keys.sort((a, b) => parseInt(a, 10) - parseInt(b, 10));
224
- const maxIndex = parseInt(sortedKeys[sortedKeys.length - 1], 10);
225
- positionalArgs = new Array(maxIndex);
226
- for (const key of sortedKeys) {
227
- const index = parseInt(key, 10) - 1;
228
- positionalArgs[index] = encodeValue(args[key]);
229
- }
230
- for (let i = 0; i < positionalArgs.length; i++) {
231
- if (positionalArgs[i] === void 0) {
232
- positionalArgs[i] = { type: "null" };
233
- }
234
- }
235
- } else {
236
- namedArgs = Object.entries(args).map(([name, value]) => ({
237
- name,
238
- value: encodeValue(value)
239
- }));
240
- }
241
- }
242
- return { args: positionalArgs, namedArgs };
243
- }
244
-
245
- // src/session.ts
246
- function normalizeUrl(url) {
247
- return url.replace(/^libsql:\/\//, "https://");
248
- }
249
- function isValidIdentifier(str) {
250
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
251
- }
252
- var Session = class {
253
- constructor(config) {
254
- this.baton = null;
255
- this.config = config;
256
- this.baseUrl = normalizeUrl(config.url);
257
- }
258
- createAbortSignal(queryOptions) {
259
- const timeout = queryOptions?.queryTimeout ?? this.config.defaultQueryTimeout;
260
- if (timeout != null && timeout > 0) {
261
- return AbortSignal.timeout(timeout);
262
- }
263
- return void 0;
264
- }
265
- /**
266
- * Describe a SQL statement to get its column metadata.
267
- *
268
- * @param sql - The SQL statement to describe
269
- * @returns Promise resolving to the statement description
270
- */
271
- async describe(sql, queryOptions) {
272
- const request = {
273
- baton: this.baton,
274
- requests: [{
275
- type: "describe",
276
- sql
277
- }]
278
- };
279
- let response;
280
- try {
281
- response = await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
282
- } catch (e) {
283
- this.baton = null;
284
- throw e;
285
- }
286
- this.baton = response.baton;
287
- if (response.base_url) {
288
- this.baseUrl = response.base_url;
289
- }
290
- if (response.results && response.results[0]) {
291
- const result = response.results[0];
292
- if (result.type === "error") {
293
- throw new DatabaseError(result.error?.message || "Describe execution failed", result.error?.code);
294
- }
295
- if (result.response?.type === "describe" && result.response.result) {
296
- return result.response.result;
297
- }
298
- }
299
- throw new DatabaseError("Unexpected describe response");
300
- }
301
- /**
302
- * Execute a SQL statement and return all results.
303
- *
304
- * @param sql - The SQL statement to execute
305
- * @param args - Optional array of parameter values or object with named parameters
306
- * @param safeIntegers - Whether to return integers as BigInt
307
- * @returns Promise resolving to the complete result set
308
- */
309
- async execute(sql, args = [], safeIntegers = false, queryOptions) {
310
- const { response, entries } = await this.executeRaw(sql, args, queryOptions);
311
- const result = await this.processCursorEntries(entries, safeIntegers);
312
- return result;
313
- }
314
- /**
315
- * Execute a SQL statement and return the raw response and entries.
316
- *
317
- * @param sql - The SQL statement to execute
318
- * @param args - Optional array of parameter values or object with named parameters
319
- * @returns Promise resolving to the raw response and cursor entries
320
- */
321
- async executeRaw(sql, args = [], queryOptions) {
322
- const encodedArgs = encodeSqlArgs(args);
323
- const request = {
324
- baton: this.baton,
325
- batch: {
326
- steps: [{
327
- stmt: {
328
- sql,
329
- args: encodedArgs.args,
330
- named_args: encodedArgs.namedArgs,
331
- want_rows: true
332
- }
333
- }]
334
- }
335
- };
336
- let result;
337
- try {
338
- result = await executeCursor(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
339
- } catch (e) {
340
- this.baton = null;
341
- throw e;
342
- }
343
- const { response, entries } = result;
344
- this.baton = response.baton;
345
- if (response.base_url) {
346
- this.baseUrl = response.base_url;
347
- }
348
- return { response, entries };
349
- }
350
- /**
351
- * Process cursor entries into a structured result.
352
- *
353
- * @param entries - Async generator of cursor entries
354
- * @returns Promise resolving to the processed result
355
- */
356
- async processCursorEntries(entries, safeIntegers = false) {
357
- let columns = [];
358
- let columnTypes = [];
359
- let rows = [];
360
- let rowsAffected = 0;
361
- let lastInsertRowid;
362
- for await (const entry of entries) {
363
- switch (entry.type) {
364
- case "step_begin":
365
- if (entry.cols) {
366
- columns = entry.cols.map((col) => col.name);
367
- columnTypes = entry.cols.map((col) => col.decltype || "");
368
- }
369
- break;
370
- case "row":
371
- if (entry.row) {
372
- const decodedRow = entry.row.map((value) => decodeValue(value, safeIntegers));
373
- const rowObject = this.createRowObject(decodedRow, columns);
374
- rows.push(rowObject);
375
- }
376
- break;
377
- case "step_end":
378
- if (entry.affected_row_count !== void 0) {
379
- rowsAffected = entry.affected_row_count;
380
- }
381
- if (entry.last_insert_rowid !== void 0 && entry.last_insert_rowid !== null) {
382
- lastInsertRowid = typeof entry.last_insert_rowid === "number" ? entry.last_insert_rowid : parseInt(entry.last_insert_rowid, 10);
383
- }
384
- break;
385
- case "step_error":
386
- case "error":
387
- throw new DatabaseError(entry.error?.message || "SQL execution failed", entry.error?.code);
388
- }
389
- }
390
- return {
391
- columns,
392
- columnTypes,
393
- rows,
394
- rowsAffected,
395
- lastInsertRowid
396
- };
397
- }
398
- /**
399
- * Create a row object with both array and named property access.
400
- *
401
- * @param values - Array of column values
402
- * @param columns - Array of column names
403
- * @returns Row object with dual access patterns
404
- */
405
- createRowObject(values, columns) {
406
- const row = [...values];
407
- columns.forEach((column, index) => {
408
- if (column && isValidIdentifier(column)) {
409
- Object.defineProperty(row, column, {
410
- value: values[index],
411
- enumerable: false,
412
- writable: false,
413
- configurable: true
414
- });
415
- }
416
- });
417
- return row;
418
- }
419
- /**
420
- * Execute multiple SQL statements in a batch.
421
- *
422
- * When `mode` is set, the batch is sent as a single Hrana request that
423
- * also carries `BEGIN <mode>` / `COMMIT` / `ROLLBACK` steps using the
424
- * server-side condition chain, giving atomic execution in one round-trip.
425
- * When `mode` is omitted, the user statements are sent as-is and run
426
- * under autocommit (or whatever transaction is already active on this
427
- * stream).
428
- *
429
- * @param statements - Array of SQL statements to execute.
430
- * @param mode - Optional locking mode; when set, the batch executes
431
- * atomically. Accepts the same values as `Database.transaction(...)`
432
- * variants: `"deferred"`, `"immediate"`, `"exclusive"`, `"concurrent"`.
433
- * @returns Promise resolving to batch execution results.
434
- */
435
- async batch(statements, mode, queryOptions) {
436
- const userSteps = statements.map((statement) => {
437
- if (typeof statement === "string") {
438
- return {
439
- stmt: { sql: statement, args: [], named_args: [], want_rows: false }
440
- };
441
- }
442
- const encodedArgs = encodeSqlArgs(statement.args ?? []);
443
- return {
444
- stmt: {
445
- sql: statement.sql,
446
- args: encodedArgs.args,
447
- named_args: encodedArgs.namedArgs,
448
- want_rows: false
449
- }
450
- };
451
- });
452
- let steps;
453
- let firstUserStepIdx = 0;
454
- let lastUserStepIdx = userSteps.length - 1;
455
- let beginIdx = -1;
456
- let commitIdx = -1;
457
- let rollbackIdx = -1;
458
- if (mode === void 0) {
459
- steps = userSteps;
460
- } else {
461
- beginIdx = 0;
462
- firstUserStepIdx = 1;
463
- lastUserStepIdx = userSteps.length;
464
- commitIdx = lastUserStepIdx + 1;
465
- rollbackIdx = commitIdx + 1;
466
- steps = [
467
- { stmt: { sql: `BEGIN ${mode.toUpperCase()}`, args: [], named_args: [], want_rows: false } },
468
- ...userSteps.map((step, i) => ({
469
- ...step,
470
- condition: { type: "ok", step: i === 0 ? beginIdx : firstUserStepIdx + i - 1 }
471
- })),
472
- {
473
- stmt: { sql: "COMMIT", args: [], named_args: [], want_rows: false },
474
- condition: { type: "ok", step: lastUserStepIdx }
475
- },
476
- {
477
- stmt: { sql: "ROLLBACK", args: [], named_args: [], want_rows: false },
478
- condition: {
479
- type: "and",
480
- conds: [
481
- { type: "ok", step: beginIdx },
482
- { type: "not", cond: { type: "ok", step: commitIdx } }
483
- ]
484
- }
485
- }
486
- ];
487
- }
488
- const request = {
489
- baton: this.baton,
490
- batch: { steps }
491
- };
492
- let batchResult;
493
- try {
494
- batchResult = await executeCursor(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
495
- } catch (e) {
496
- this.baton = null;
497
- throw e;
498
- }
499
- const { response, entries } = batchResult;
500
- this.baton = response.baton;
501
- if (response.base_url) {
502
- this.baseUrl = response.base_url;
503
- }
504
- let totalRowsAffected = 0;
505
- let lastInsertRowid;
506
- let deferredError = null;
507
- let currentStep;
508
- const isUserStep = (step) => {
509
- if (mode === void 0) {
510
- return true;
511
- }
512
- return step !== void 0 && step >= firstUserStepIdx && step <= lastUserStepIdx;
513
- };
514
- for await (const entry of entries) {
515
- switch (entry.type) {
516
- case "step_begin":
517
- currentStep = entry.step;
518
- break;
519
- case "step_end":
520
- if (isUserStep(currentStep)) {
521
- if (entry.affected_row_count !== void 0) {
522
- totalRowsAffected += entry.affected_row_count;
523
- }
524
- if (entry.last_insert_rowid !== void 0 && entry.last_insert_rowid !== null) {
525
- lastInsertRowid = typeof entry.last_insert_rowid === "number" ? entry.last_insert_rowid : parseInt(entry.last_insert_rowid, 10);
526
- }
527
- }
528
- currentStep = void 0;
529
- break;
530
- case "step_error":
531
- if (mode === void 0) {
532
- throw new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
533
- }
534
- if (deferredError === null && entry.step !== rollbackIdx) {
535
- deferredError = new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
536
- }
537
- currentStep = void 0;
538
- break;
539
- case "error":
540
- throw new DatabaseError(entry.error?.message || "Batch execution failed", entry.error?.code);
541
- }
542
- }
543
- if (deferredError !== null) {
544
- throw deferredError;
545
- }
546
- return {
547
- rowsAffected: totalRowsAffected,
548
- lastInsertRowid
549
- };
550
- }
551
- /**
552
- * Execute a sequence of SQL statements separated by semicolons.
553
- *
554
- * @param sql - SQL string containing multiple statements separated by semicolons
555
- * @returns Promise resolving when all statements are executed
556
- */
557
- async sequence(sql, queryOptions) {
558
- const request = {
559
- baton: this.baton,
560
- requests: [{
561
- type: "sequence",
562
- sql
563
- }]
564
- };
565
- let seqResponse;
566
- try {
567
- seqResponse = await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey, this.createAbortSignal(queryOptions));
568
- } catch (e) {
569
- this.baton = null;
570
- throw e;
571
- }
572
- this.baton = seqResponse.baton;
573
- if (seqResponse.base_url) {
574
- this.baseUrl = seqResponse.base_url;
575
- }
576
- if (seqResponse.results && seqResponse.results[0]) {
577
- const result = seqResponse.results[0];
578
- if (result.type === "error") {
579
- throw new DatabaseError(result.error?.message || "Sequence execution failed", result.error?.code);
580
- }
581
- }
582
- }
583
- /**
584
- * Close the session.
585
- *
586
- * This sends a close request to the server to properly clean up the stream
587
- * before resetting the local state.
588
- */
589
- async close() {
590
- if (this.baton) {
591
- try {
592
- const request = {
593
- baton: this.baton,
594
- requests: [{
595
- type: "close"
596
- }]
597
- };
598
- await executePipeline(this.baseUrl, this.config.authToken, request, this.config.remoteEncryptionKey);
599
- } catch {
600
- }
601
- }
602
- this.baton = null;
603
- this.baseUrl = "";
604
- }
605
- };
606
-
607
- // src/async-lock.ts
608
- var AsyncLock = class {
609
- constructor() {
610
- this.locked = false;
611
- this.queue = [];
612
- }
613
- async acquire() {
614
- if (!this.locked) {
615
- this.locked = true;
616
- return;
617
- }
618
- return new Promise((resolve) => {
619
- this.queue.push(resolve);
620
- });
621
- }
622
- release() {
623
- const next = this.queue.shift();
624
- if (next) {
625
- next();
626
- } else {
627
- this.locked = false;
628
- }
629
- }
630
- };
631
-
632
- // src/compat.ts
633
- var LibsqlError = class extends Error {
634
- constructor(message, code, extendedCode, rawCode, cause) {
635
- super(message);
636
- this.name = "LibsqlError";
637
- this.code = code;
638
- this.extendedCode = extendedCode;
639
- this.rawCode = rawCode;
640
- this.cause = cause;
641
- }
642
- };
643
- var LibSQLClient = class {
644
- constructor(config) {
645
- this.execLock = new AsyncLock();
646
- this._closed = false;
647
- this._defaultSafeIntegers = false;
648
- this.validateConfig(config);
649
- const sessionConfig = {
650
- url: config.url,
651
- authToken: config.authToken || "",
652
- remoteEncryptionKey: config.remoteEncryptionKey
653
- };
654
- this.sessionConfig = sessionConfig;
655
- this.session = new Session(sessionConfig);
656
- }
657
- validateConfig(config) {
658
- const unsupportedOptions = [];
659
- if (config.encryptionKey !== void 0) {
660
- unsupportedOptions.push({ key: "encryptionKey", value: config.encryptionKey });
661
- }
662
- if (config.syncUrl !== void 0) {
663
- unsupportedOptions.push({ key: "syncUrl", value: config.syncUrl });
664
- }
665
- if (config.syncInterval !== void 0) {
666
- unsupportedOptions.push({ key: "syncInterval", value: config.syncInterval });
667
- }
668
- if (config.readYourWrites !== void 0) {
669
- unsupportedOptions.push({ key: "readYourWrites", value: config.readYourWrites });
670
- }
671
- if (config.offline !== void 0) {
672
- unsupportedOptions.push({ key: "offline", value: config.offline });
673
- }
674
- if (config.tls !== void 0) {
675
- unsupportedOptions.push({ key: "tls", value: config.tls });
676
- }
677
- if (config.intMode !== void 0) {
678
- unsupportedOptions.push({ key: "intMode", value: config.intMode });
679
- }
680
- if (config.fetch !== void 0) {
681
- unsupportedOptions.push({ key: "fetch", value: config.fetch });
682
- }
683
- if (config.concurrency !== void 0) {
684
- unsupportedOptions.push({ key: "concurrency", value: config.concurrency });
685
- }
686
- if (unsupportedOptions.length > 0) {
687
- const optionsList = unsupportedOptions.map((opt) => `'${opt.key}'`).join(", ");
688
- throw new LibsqlError(
689
- `Unsupported configuration options: ${optionsList}. Only 'url', 'authToken', and 'remoteEncryptionKey' are supported in the serverless compatibility layer.`,
690
- "UNSUPPORTED_CONFIG"
691
- );
692
- }
693
- if (!config.url) {
694
- throw new LibsqlError("Missing required 'url' configuration option", "MISSING_URL");
695
- }
696
- }
697
- get closed() {
698
- return this._closed;
699
- }
700
- get protocol() {
701
- return "http";
702
- }
703
- normalizeStatement(stmt) {
704
- if (typeof stmt === "string") {
705
- return { sql: stmt, args: [] };
706
- }
707
- const args = stmt.args || [];
708
- if (Array.isArray(args)) {
709
- return { sql: stmt.sql, args };
710
- }
711
- return { sql: stmt.sql, args: Object.values(args) };
712
- }
713
- convertResult(result) {
714
- const resultSet = {
715
- columns: result.columns || [],
716
- columnTypes: result.columnTypes || [],
717
- rows: result.rows || [],
718
- rowsAffected: result.rowsAffected || 0,
719
- lastInsertRowid: result.lastInsertRowid != null ? BigInt(result.lastInsertRowid) : void 0,
720
- toJSON() {
721
- return {
722
- columns: this.columns,
723
- columnTypes: this.columnTypes,
724
- rows: this.rows,
725
- rowsAffected: this.rowsAffected,
726
- lastInsertRowid: this.lastInsertRowid?.toString()
727
- };
728
- }
729
- };
730
- return resultSet;
731
- }
732
- async execute(stmtOrSql, args) {
733
- await this.execLock.acquire();
734
- try {
735
- if (this._closed) {
736
- throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
737
- }
738
- let normalizedStmt;
739
- if (typeof stmtOrSql === "string") {
740
- const normalizedArgs = args ? Array.isArray(args) ? args : Object.values(args) : [];
741
- normalizedStmt = { sql: stmtOrSql, args: normalizedArgs };
742
- } else {
743
- normalizedStmt = this.normalizeStatement(stmtOrSql);
744
- }
745
- const result = await this.session.execute(normalizedStmt.sql, normalizedStmt.args, this._defaultSafeIntegers);
746
- return this.convertResult(result);
747
- } catch (error) {
748
- if (error instanceof LibsqlError) {
749
- throw error;
750
- }
751
- throw mapDatabaseError(error, "EXECUTE_ERROR");
752
- } finally {
753
- this.execLock.release();
754
- }
755
- }
756
- async batch(stmts, mode) {
757
- await this.execLock.acquire();
758
- try {
759
- if (this._closed) {
760
- throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
761
- }
762
- const sqlStatements = stmts.map((stmt) => {
763
- const normalized = this.normalizeStatement(stmt);
764
- return normalized.sql;
765
- });
766
- const result = await this.session.batch(sqlStatements);
767
- return [this.convertResult(result)];
768
- } catch (error) {
769
- if (error instanceof LibsqlError) {
770
- throw error;
771
- }
772
- throw mapDatabaseError(error, "BATCH_ERROR");
773
- } finally {
774
- this.execLock.release();
775
- }
776
- }
777
- async migrate(stmts) {
778
- return this.batch(stmts, "write");
779
- }
780
- modeToBeginSql(mode) {
781
- switch (mode) {
782
- case "write":
783
- return "BEGIN IMMEDIATE";
784
- case "deferred":
785
- return "BEGIN DEFERRED";
786
- case "read":
787
- default:
788
- return "BEGIN";
789
- }
790
- }
791
- async transaction(mode) {
792
- await this.execLock.acquire();
793
- if (this._closed) {
794
- this.execLock.release();
795
- throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
796
- }
797
- const txSession = new Session(this.sessionConfig);
798
- let txClosed = false;
799
- let cleanupStarted = false;
800
- const ensureOpen = () => {
801
- if (txClosed) {
802
- throw new LibsqlError("Transaction is closed", "TRANSACTION_CLOSED");
803
- }
804
- };
805
- const closeTx = async () => {
806
- if (cleanupStarted) return;
807
- cleanupStarted = true;
808
- txClosed = true;
809
- try {
810
- await txSession.close();
811
- } finally {
812
- this.execLock.release();
813
- }
814
- };
815
- const executeInTx = async (stmt) => {
816
- ensureOpen();
817
- const normalized = this.normalizeStatement(stmt);
818
- try {
819
- const result = await txSession.execute(normalized.sql, normalized.args, this._defaultSafeIntegers);
820
- return this.convertResult(result);
821
- } catch (error) {
822
- throw mapDatabaseError(error, "EXECUTE_ERROR");
823
- }
824
- };
825
- try {
826
- await txSession.sequence(this.modeToBeginSql(mode));
827
- } catch (error) {
828
- await closeTx();
829
- throw mapDatabaseError(error, "BEGIN_ERROR");
830
- }
831
- return {
832
- execute: async (stmtOrSql, args) => {
833
- if (typeof stmtOrSql === "string") {
834
- const normalizedArgs = args ? Array.isArray(args) ? args : Object.values(args) : [];
835
- return executeInTx({ sql: stmtOrSql, args: normalizedArgs });
836
- }
837
- return executeInTx(stmtOrSql);
838
- },
839
- batch: async (stmts) => {
840
- ensureOpen();
841
- const results = [];
842
- for (const stmt of stmts) {
843
- results.push(await executeInTx(stmt));
844
- }
845
- return results;
846
- },
847
- executeMultiple: async (sql) => {
848
- ensureOpen();
849
- try {
850
- await txSession.sequence(sql);
851
- } catch (error) {
852
- throw mapDatabaseError(error, "EXECUTE_MULTIPLE_ERROR");
853
- }
854
- },
855
- commit: async () => {
856
- ensureOpen();
857
- try {
858
- await txSession.sequence("COMMIT");
859
- } catch (error) {
860
- throw mapDatabaseError(error, "COMMIT_ERROR");
861
- } finally {
862
- await closeTx();
863
- }
864
- },
865
- rollback: async () => {
866
- ensureOpen();
867
- try {
868
- await txSession.sequence("ROLLBACK");
869
- } catch (error) {
870
- throw mapDatabaseError(error, "ROLLBACK_ERROR");
871
- } finally {
872
- await closeTx();
873
- }
874
- },
875
- close: () => {
876
- if (txClosed) return;
877
- txClosed = true;
878
- void txSession.sequence("ROLLBACK").catch(() => void 0).finally(() => {
879
- void closeTx();
880
- });
881
- },
882
- get closed() {
883
- return txClosed;
884
- }
885
- };
886
- }
887
- async executeMultiple(sql) {
888
- await this.execLock.acquire();
889
- try {
890
- if (this._closed) {
891
- throw new LibsqlError("Client is closed", "CLIENT_CLOSED");
892
- }
893
- await this.session.sequence(sql);
894
- } catch (error) {
895
- if (error instanceof LibsqlError) {
896
- throw error;
897
- }
898
- throw mapDatabaseError(error, "EXECUTE_MULTIPLE_ERROR");
899
- } finally {
900
- this.execLock.release();
901
- }
902
- }
903
- async sync() {
904
- throw new LibsqlError("Sync not supported for remote databases", "NOT_SUPPORTED");
905
- }
906
- close() {
907
- this._closed = true;
908
- this.session.close().catch((error) => {
909
- console.error("Error closing session:", error);
910
- });
911
- }
912
- };
913
- function createClient(config) {
914
- return new LibSQLClient(config);
915
- }
916
- var sqliteBaseErrorCodes = /* @__PURE__ */ new Set([
917
- "SQLITE_ERROR",
918
- "SQLITE_INTERNAL",
919
- "SQLITE_PERM",
920
- "SQLITE_ABORT",
921
- "SQLITE_BUSY",
922
- "SQLITE_LOCKED",
923
- "SQLITE_NOMEM",
924
- "SQLITE_READONLY",
925
- "SQLITE_INTERRUPT",
926
- "SQLITE_IOERR",
927
- "SQLITE_CORRUPT",
928
- "SQLITE_NOTFOUND",
929
- "SQLITE_FULL",
930
- "SQLITE_CANTOPEN",
931
- "SQLITE_PROTOCOL",
932
- "SQLITE_EMPTY",
933
- "SQLITE_SCHEMA",
934
- "SQLITE_TOOBIG",
935
- "SQLITE_CONSTRAINT",
936
- "SQLITE_MISMATCH",
937
- "SQLITE_MISUSE",
938
- "SQLITE_NOLFS",
939
- "SQLITE_AUTH",
940
- "SQLITE_FORMAT",
941
- "SQLITE_RANGE",
942
- "SQLITE_NOTADB",
943
- "SQLITE_NOTICE",
944
- "SQLITE_WARNING"
945
- ]);
946
- function parseErrorCode(serverCode) {
947
- if (sqliteBaseErrorCodes.has(serverCode)) {
948
- return { code: serverCode };
949
- }
950
- for (const base of sqliteBaseErrorCodes) {
951
- if (serverCode.startsWith(base + "_")) {
952
- return { code: base, extendedCode: serverCode };
953
- }
954
- }
955
- return { code: serverCode };
956
- }
957
- function mapDatabaseError(error, fallbackCode) {
958
- if (error instanceof DatabaseError && error.code) {
959
- const { code, extendedCode } = parseErrorCode(error.code);
960
- return new LibsqlError(error.message, code, extendedCode, error.rawCode, error);
961
- }
962
- const cause = error instanceof Error ? error : void 0;
963
- return new LibsqlError(error.message ?? String(error), fallbackCode, void 0, void 0, cause);
964
- }
965
-
966
- export { LibsqlError, createClient };
1
+ export * from '../compat.js';