@ohos-ports/libsql-client 0.18.0-beta.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.
@@ -0,0 +1,695 @@
1
+ import Database from "@ohos-ports/libsql";
2
+ import { Buffer } from "node:buffer";
3
+ import { LibsqlError, LibsqlBatchError } from "@libsql/core/api";
4
+ import { expandConfig, isInMemoryConfig } from "@libsql/core/config";
5
+ import { supportedUrlLink, transactionModeToBegin, ResultSetImpl, } from "@libsql/core/util";
6
+ export * from "@libsql/core/api";
7
+ export function createClient(config) {
8
+ return _createClient(expandConfig(config, true));
9
+ }
10
+ /** @private */
11
+ export function _createClient(config) {
12
+ if (config.scheme !== "file") {
13
+ throw new LibsqlError(`URL scheme ${JSON.stringify(config.scheme + ":")} is not supported by the local sqlite3 client. ` +
14
+ `For more information, please read ${supportedUrlLink}`, "URL_SCHEME_NOT_SUPPORTED");
15
+ }
16
+ const authority = config.authority;
17
+ if (authority !== undefined) {
18
+ const host = authority.host.toLowerCase();
19
+ if (host !== "" && host !== "localhost") {
20
+ throw new LibsqlError(`Invalid host in file URL: ${JSON.stringify(authority.host)}. ` +
21
+ 'A "file:" URL with an absolute path should start with one slash ("file:/absolute/path.db") ' +
22
+ 'or with three slashes ("file:///absolute/path.db"). ' +
23
+ `For more information, please read ${supportedUrlLink}`, "URL_INVALID");
24
+ }
25
+ if (authority.port !== undefined) {
26
+ throw new LibsqlError("File URL cannot have a port", "URL_INVALID");
27
+ }
28
+ if (authority.userinfo !== undefined) {
29
+ throw new LibsqlError("File URL cannot have username and password", "URL_INVALID");
30
+ }
31
+ }
32
+ let isInMemory = isInMemoryConfig(config);
33
+ if (isInMemory && config.syncUrl) {
34
+ throw new LibsqlError(`Embedded replica must use file for local db but URI with in-memory mode were provided instead: ${config.path}`, "URL_INVALID");
35
+ }
36
+ let path = config.path;
37
+ if (isInMemory) {
38
+ // note: we should prepend file scheme in order for SQLite3 to recognize :memory: connection query parameters
39
+ path = `${config.scheme}:${config.path}`;
40
+ }
41
+ // An in-memory database exists only on the connection that opened it, so a
42
+ // second connection would be a second, empty database rather than another
43
+ // way into the same one. Each connection to an embedded replica carries
44
+ // its own sync state. Both are therefore single-connection databases.
45
+ const maxConnections = isInMemory || config.syncUrl ? 1 : Math.max(1, config.concurrency);
46
+ const options = {
47
+ authToken: config.authToken,
48
+ encryptionKey: config.encryptionKey,
49
+ remoteEncryptionKey: config.remoteEncryptionKey,
50
+ syncUrl: config.syncUrl,
51
+ syncPeriod: config.syncInterval,
52
+ readYourWrites: config.readYourWrites,
53
+ offline: config.offline,
54
+ timeout: config.timeout,
55
+ };
56
+ const pool = new ConnectionPool(path, options, maxConnections);
57
+ // fail fast if the database cannot be opened at all
58
+ const db = pool.acquireSync();
59
+ try {
60
+ executeStmt(db, "SELECT 1 AS checkThatTheDatabaseCanBeOpened", config.intMode);
61
+ }
62
+ catch (e) {
63
+ pool.close();
64
+ throw e;
65
+ }
66
+ pool.release(db);
67
+ return new Sqlite3Client(pool, config.intMode);
68
+ }
69
+ /**
70
+ * A client owns a small pool of connections to one database, in the same way
71
+ * that a remote client owns a set of hrana streams: every client operation
72
+ * borrows one for the duration of the call, and a transaction borrows one for
73
+ * its lifetime and gives it back on commit, rollback or close. Nothing is
74
+ * shared between an open transaction and the rest of the client.
75
+ *
76
+ * @private
77
+ */
78
+ export class ConnectionPool {
79
+ #path;
80
+ #options;
81
+ #maxConnections;
82
+ #idle;
83
+ #borrowed;
84
+ // the subset of `#borrowed` held by an open transaction
85
+ #heldByTransaction;
86
+ #waiters;
87
+ #closed;
88
+ constructor(path, options, maxConnections) {
89
+ this.#path = path;
90
+ this.#options = options;
91
+ this.#maxConnections = maxConnections;
92
+ this.#idle = [];
93
+ this.#borrowed = new Set();
94
+ this.#heldByTransaction = new Set();
95
+ this.#waiters = [];
96
+ this.#closed = false;
97
+ }
98
+ // Borrows a connection, opening one if the pool is below its limit and
99
+ // waiting for a release if it is not.
100
+ //
101
+ // Every borrow but a transaction's is a short synchronous call that
102
+ // returns the connection before the caller sees it again, so waiting for
103
+ // one is safe. A transaction holds its connection until the caller commits
104
+ // or rolls back, so if transactions hold every connection there is nothing
105
+ // to wait for - the caller has to act first. Say so instead of hanging.
106
+ acquire(forTransaction = false) {
107
+ if (!this.#closed && this.#atLimit()) {
108
+ if (this.#heldByTransaction.size >= this.#maxConnections) {
109
+ return Promise.reject(new LibsqlError(this.#maxConnections === 1
110
+ ? "This client has a single connection, which an open transaction is holding. " +
111
+ "In-memory databases and embedded replicas cannot have more than one. " +
112
+ "Commit or roll back the transaction before using the client again."
113
+ : `All ${this.#maxConnections} of this client's connections are held by open transactions. ` +
114
+ "Commit or roll back one before using the client again, or raise `concurrency`.", "TRANSACTION_ACTIVE"));
115
+ }
116
+ return new Promise((resolve, reject) => this.#waiters.push({
117
+ resolve: (db) => {
118
+ if (forTransaction) {
119
+ this.#heldByTransaction.add(db);
120
+ }
121
+ resolve(db);
122
+ },
123
+ reject,
124
+ }));
125
+ }
126
+ try {
127
+ return Promise.resolve(this.acquireSync(forTransaction));
128
+ }
129
+ catch (e) {
130
+ return Promise.reject(e);
131
+ }
132
+ }
133
+ // Borrows a connection without waiting. Only valid when the pool cannot be
134
+ // at its limit yet, which is why it is used for the initial probe.
135
+ acquireSync(forTransaction = false) {
136
+ this.#checkNotClosed();
137
+ const idle = this.#idle.pop();
138
+ const db = idle ?? new Database(this.#path, this.#options);
139
+ this.#borrowed.add(db);
140
+ if (forTransaction) {
141
+ this.#heldByTransaction.add(db);
142
+ }
143
+ return db;
144
+ }
145
+ // Returns a borrowed connection to the pool, handing it straight to a
146
+ // waiter if there is one.
147
+ release(db) {
148
+ this.#borrowed.delete(db);
149
+ this.#heldByTransaction.delete(db);
150
+ // A connection must never go back into the pool mid-transaction, or
151
+ // the next borrower would silently inherit it.
152
+ if (db.open && db.inTransaction) {
153
+ try {
154
+ db.prepare("ROLLBACK").run();
155
+ }
156
+ catch {
157
+ // the connection is unusable; drop it rather than reuse it
158
+ closeQuietly(db);
159
+ return;
160
+ }
161
+ }
162
+ if (this.#closed || !db.open) {
163
+ closeQuietly(db);
164
+ return;
165
+ }
166
+ const waiter = this.#waiters.shift();
167
+ if (waiter !== undefined) {
168
+ this.#borrowed.add(db);
169
+ waiter.resolve(db);
170
+ }
171
+ else {
172
+ this.#idle.push(db);
173
+ }
174
+ }
175
+ close() {
176
+ this.#closed = true;
177
+ for (const db of this.#idle) {
178
+ closeQuietly(db);
179
+ }
180
+ this.#idle = [];
181
+ for (const db of this.#borrowed) {
182
+ closeQuietly(db);
183
+ }
184
+ this.#borrowed.clear();
185
+ this.#heldByTransaction.clear();
186
+ // Anything still queued will never be served now. Reject it: dropping
187
+ // the callbacks would leave those operations pending forever.
188
+ const waiters = this.#waiters;
189
+ this.#waiters = [];
190
+ for (const waiter of waiters) {
191
+ waiter.reject(new LibsqlError("The client is closed", "CLIENT_CLOSED"));
192
+ }
193
+ }
194
+ reopen() {
195
+ this.close();
196
+ this.#closed = false;
197
+ }
198
+ #atLimit() {
199
+ return (this.#idle.length === 0 &&
200
+ this.#borrowed.size >= this.#maxConnections);
201
+ }
202
+ #checkNotClosed() {
203
+ if (this.#closed) {
204
+ throw new LibsqlError("The client is closed", "CLIENT_CLOSED");
205
+ }
206
+ }
207
+ }
208
+ function closeQuietly(db) {
209
+ try {
210
+ if (db.open) {
211
+ db.close();
212
+ }
213
+ }
214
+ catch {
215
+ // nothing useful to do while tearing down
216
+ }
217
+ }
218
+ export class Sqlite3Client {
219
+ #pool;
220
+ #intMode;
221
+ closed;
222
+ protocol;
223
+ /** @private */
224
+ constructor(pool, intMode) {
225
+ this.#pool = pool;
226
+ this.#intMode = intMode;
227
+ this.closed = false;
228
+ this.protocol = "file";
229
+ }
230
+ async execute(stmtOrSql, args) {
231
+ let stmt;
232
+ if (typeof stmtOrSql === "string") {
233
+ stmt = {
234
+ sql: stmtOrSql,
235
+ args: args || [],
236
+ };
237
+ }
238
+ else {
239
+ stmt = stmtOrSql;
240
+ }
241
+ this.#checkNotClosed();
242
+ const db = await this.#pool.acquire();
243
+ try {
244
+ this.#checkUsable(db);
245
+ return executeStmt(db, stmt, this.#intMode);
246
+ }
247
+ finally {
248
+ this.#pool.release(db);
249
+ }
250
+ }
251
+ async batch(stmts, mode = "deferred") {
252
+ this.#checkNotClosed();
253
+ const db = await this.#pool.acquire();
254
+ try {
255
+ this.#checkUsable(db);
256
+ executeStmt(db, transactionModeToBegin(mode), this.#intMode);
257
+ const resultSets = [];
258
+ for (let i = 0; i < stmts.length; i++) {
259
+ try {
260
+ if (!db.inTransaction) {
261
+ throw new LibsqlBatchError("The transaction has been rolled back", i, "TRANSACTION_CLOSED");
262
+ }
263
+ const stmt = stmts[i];
264
+ const normalizedStmt = Array.isArray(stmt)
265
+ ? { sql: stmt[0], args: stmt[1] || [] }
266
+ : stmt;
267
+ resultSets.push(executeStmt(db, normalizedStmt, this.#intMode));
268
+ }
269
+ catch (e) {
270
+ if (e instanceof LibsqlBatchError) {
271
+ throw e;
272
+ }
273
+ if (e instanceof LibsqlError) {
274
+ throw new LibsqlBatchError(e.message, i, e.code, e.extendedCode, e.rawCode, e.cause instanceof Error ? e.cause : undefined);
275
+ }
276
+ throw e;
277
+ }
278
+ }
279
+ executeStmt(db, "COMMIT", this.#intMode);
280
+ return resultSets;
281
+ }
282
+ finally {
283
+ // `release` rolls back anything still open before reuse
284
+ this.#pool.release(db);
285
+ }
286
+ }
287
+ async migrate(stmts) {
288
+ this.#checkNotClosed();
289
+ const db = await this.#pool.acquire();
290
+ try {
291
+ this.#checkUsable(db);
292
+ executeStmt(db, "PRAGMA foreign_keys=off", this.#intMode);
293
+ executeStmt(db, transactionModeToBegin("deferred"), this.#intMode);
294
+ const resultSets = [];
295
+ for (let i = 0; i < stmts.length; i++) {
296
+ try {
297
+ if (!db.inTransaction) {
298
+ throw new LibsqlBatchError("The transaction has been rolled back", i, "TRANSACTION_CLOSED");
299
+ }
300
+ resultSets.push(executeStmt(db, stmts[i], this.#intMode));
301
+ }
302
+ catch (e) {
303
+ if (e instanceof LibsqlBatchError) {
304
+ throw e;
305
+ }
306
+ if (e instanceof LibsqlError) {
307
+ throw new LibsqlBatchError(e.message, i, e.code, e.extendedCode, e.rawCode, e.cause instanceof Error ? e.cause : undefined);
308
+ }
309
+ throw e;
310
+ }
311
+ }
312
+ executeStmt(db, "COMMIT", this.#intMode);
313
+ return resultSets;
314
+ }
315
+ finally {
316
+ if (db.inTransaction) {
317
+ executeStmt(db, "ROLLBACK", this.#intMode);
318
+ }
319
+ executeStmt(db, "PRAGMA foreign_keys=on", this.#intMode);
320
+ this.#pool.release(db);
321
+ }
322
+ }
323
+ async transaction(mode = "write") {
324
+ this.#checkNotClosed();
325
+ const db = await this.#pool.acquire(true);
326
+ try {
327
+ this.#checkUsable(db);
328
+ executeStmt(db, transactionModeToBegin(mode), this.#intMode);
329
+ }
330
+ catch (e) {
331
+ this.#pool.release(db);
332
+ throw e;
333
+ }
334
+ // The transaction owns this connection until it settles, exactly as an
335
+ // `HttpTransaction` owns its stream.
336
+ return new Sqlite3Transaction(db, this.#intMode, (used) => this.#pool.release(used));
337
+ }
338
+ async executeMultiple(sql) {
339
+ this.#checkNotClosed();
340
+ const db = await this.#pool.acquire();
341
+ try {
342
+ this.#checkUsable(db);
343
+ return executeMultiple(db, sql);
344
+ }
345
+ finally {
346
+ // `release` rolls back a transaction `sql` left open
347
+ this.#pool.release(db);
348
+ }
349
+ }
350
+ async sync() {
351
+ this.#checkNotClosed();
352
+ const db = await this.#pool.acquire();
353
+ try {
354
+ this.#checkUsable(db);
355
+ const rep = await db.sync();
356
+ return {
357
+ frames_synced: rep.frames_synced,
358
+ frame_no: rep.frame_no,
359
+ };
360
+ }
361
+ finally {
362
+ this.#pool.release(db);
363
+ }
364
+ }
365
+ async reconnect() {
366
+ this.#pool.reopen();
367
+ this.closed = false;
368
+ }
369
+ close() {
370
+ this.closed = true;
371
+ this.#pool.close();
372
+ }
373
+ #checkNotClosed() {
374
+ if (this.closed) {
375
+ throw new LibsqlError("The client is closed", "CLIENT_CLOSED");
376
+ }
377
+ }
378
+ // `close()` and `reconnect()` are synchronous and can land between a
379
+ // borrow and the work it was borrowed for, closing the connection under an
380
+ // operation that is already holding one. Without this the operation
381
+ // reaches libsql with a closed handle and fails with a raw TypeError.
382
+ #checkUsable(db) {
383
+ this.#checkNotClosed();
384
+ if (!db.open) {
385
+ throw new LibsqlError("The connection was closed while this operation was in flight", "CLIENT_CLOSED");
386
+ }
387
+ }
388
+ }
389
+ export class Sqlite3Transaction {
390
+ // null once the connection has been returned to the pool
391
+ #database;
392
+ #intMode;
393
+ #release;
394
+ /** @private */
395
+ constructor(database, intMode, release) {
396
+ this.#database = database;
397
+ this.#intMode = intMode;
398
+ this.#release = release;
399
+ }
400
+ // Returns the connection to the pool. Idempotent, so every exit path can
401
+ // call it without checking whether another already did.
402
+ #settle() {
403
+ const db = this.#database;
404
+ if (db === null) {
405
+ return;
406
+ }
407
+ this.#database = null;
408
+ this.#release(db);
409
+ }
410
+ #getDatabase() {
411
+ this.#checkNotClosed();
412
+ return this.#database;
413
+ }
414
+ async execute(stmtOrSql, args) {
415
+ let stmt;
416
+ if (typeof stmtOrSql === "string") {
417
+ stmt = {
418
+ sql: stmtOrSql,
419
+ args: args || [],
420
+ };
421
+ }
422
+ else {
423
+ stmt = stmtOrSql;
424
+ }
425
+ return executeStmt(this.#getDatabase(), stmt, this.#intMode);
426
+ }
427
+ async batch(stmts) {
428
+ const resultSets = [];
429
+ for (let i = 0; i < stmts.length; i++) {
430
+ try {
431
+ const db = this.#getDatabase();
432
+ const stmt = stmts[i];
433
+ const normalizedStmt = Array.isArray(stmt)
434
+ ? { sql: stmt[0], args: stmt[1] || [] }
435
+ : stmt;
436
+ resultSets.push(executeStmt(db, normalizedStmt, this.#intMode));
437
+ }
438
+ catch (e) {
439
+ if (e instanceof LibsqlBatchError) {
440
+ throw e;
441
+ }
442
+ if (e instanceof LibsqlError) {
443
+ throw new LibsqlBatchError(e.message, i, e.code, e.extendedCode, e.rawCode, e.cause instanceof Error ? e.cause : undefined);
444
+ }
445
+ throw e;
446
+ }
447
+ }
448
+ return resultSets;
449
+ }
450
+ async executeMultiple(sql) {
451
+ return executeMultiple(this.#getDatabase(), sql);
452
+ }
453
+ async rollback() {
454
+ const db = this.#database;
455
+ if (db === null || !db.open) {
456
+ this.#settle();
457
+ return;
458
+ }
459
+ try {
460
+ this.#checkNotClosed();
461
+ executeStmt(db, "ROLLBACK", this.#intMode);
462
+ }
463
+ finally {
464
+ this.#settle();
465
+ }
466
+ }
467
+ async commit() {
468
+ try {
469
+ executeStmt(this.#getDatabase(), "COMMIT", this.#intMode);
470
+ }
471
+ finally {
472
+ this.#settle();
473
+ }
474
+ }
475
+ close() {
476
+ const db = this.#database;
477
+ if (db === null) {
478
+ return;
479
+ }
480
+ try {
481
+ // `client.close()` may have closed this connection already, and
482
+ // reading `inTransaction` on a closed database aborts the process.
483
+ if (db.open && db.inTransaction) {
484
+ executeStmt(db, "ROLLBACK", this.#intMode);
485
+ }
486
+ }
487
+ finally {
488
+ this.#settle();
489
+ }
490
+ }
491
+ get closed() {
492
+ const db = this.#database;
493
+ if (db === null || !db.open) {
494
+ return true;
495
+ }
496
+ return !db.inTransaction;
497
+ }
498
+ #checkNotClosed() {
499
+ if (this.closed) {
500
+ throw new LibsqlError("The transaction is closed", "TRANSACTION_CLOSED");
501
+ }
502
+ }
503
+ }
504
+ function executeStmt(db, stmt, intMode) {
505
+ let sql;
506
+ let args;
507
+ if (typeof stmt === "string") {
508
+ sql = stmt;
509
+ args = [];
510
+ }
511
+ else {
512
+ sql = stmt.sql;
513
+ if (Array.isArray(stmt.args)) {
514
+ args = stmt.args.map((value) => valueToSql(value, intMode));
515
+ }
516
+ else {
517
+ args = {};
518
+ for (const name in stmt.args) {
519
+ const argName = name[0] === "@" || name[0] === "$" || name[0] === ":"
520
+ ? name.substring(1)
521
+ : name;
522
+ args[argName] = valueToSql(stmt.args[name], intMode);
523
+ }
524
+ }
525
+ }
526
+ try {
527
+ const sqlStmt = db.prepare(sql);
528
+ sqlStmt.safeIntegers(true);
529
+ let returnsData = true;
530
+ try {
531
+ sqlStmt.raw(true);
532
+ }
533
+ catch {
534
+ // raw() throws an exception if the statement does not return data
535
+ returnsData = false;
536
+ }
537
+ if (returnsData) {
538
+ const columns = Array.from(sqlStmt.columns().map((col) => col.name));
539
+ const columnTypes = Array.from(sqlStmt.columns().map((col) => col.type ?? ""));
540
+ const rows = sqlStmt.all(args).map((sqlRow) => {
541
+ return rowFromSql(sqlRow, columns, intMode);
542
+ });
543
+ // TODO: can we get this info from better-sqlite3?
544
+ const rowsAffected = 0;
545
+ const lastInsertRowid = undefined;
546
+ return new ResultSetImpl(columns, columnTypes, rows, rowsAffected, lastInsertRowid);
547
+ }
548
+ else {
549
+ const info = sqlStmt.run(args);
550
+ const rowsAffected = info.changes;
551
+ const lastInsertRowid = BigInt(info.lastInsertRowid);
552
+ return new ResultSetImpl([], [], [], rowsAffected, lastInsertRowid);
553
+ }
554
+ }
555
+ catch (e) {
556
+ throw mapSqliteError(e);
557
+ }
558
+ }
559
+ function rowFromSql(sqlRow, columns, intMode) {
560
+ const row = {};
561
+ // make sure that the "length" property is not enumerable
562
+ Object.defineProperty(row, "length", { value: sqlRow.length });
563
+ for (let i = 0; i < sqlRow.length; ++i) {
564
+ const value = valueFromSql(sqlRow[i], intMode);
565
+ Object.defineProperty(row, i, { value });
566
+ const column = columns[i];
567
+ if (!Object.hasOwn(row, column)) {
568
+ Object.defineProperty(row, column, {
569
+ value,
570
+ enumerable: true,
571
+ configurable: true,
572
+ writable: true,
573
+ });
574
+ }
575
+ }
576
+ return row;
577
+ }
578
+ function valueFromSql(sqlValue, intMode) {
579
+ if (typeof sqlValue === "bigint") {
580
+ if (intMode === "number") {
581
+ if (sqlValue < minSafeBigint || sqlValue > maxSafeBigint) {
582
+ throw new RangeError("Received integer which cannot be safely represented as a JavaScript number");
583
+ }
584
+ return Number(sqlValue);
585
+ }
586
+ else if (intMode === "bigint") {
587
+ return sqlValue;
588
+ }
589
+ else if (intMode === "string") {
590
+ return "" + sqlValue;
591
+ }
592
+ else {
593
+ throw new Error("Invalid value for IntMode");
594
+ }
595
+ }
596
+ else if (sqlValue instanceof Buffer) {
597
+ return sqlValue.buffer;
598
+ }
599
+ return sqlValue;
600
+ }
601
+ const minSafeBigint = -9007199254740991n;
602
+ const maxSafeBigint = 9007199254740991n;
603
+ function valueToSql(value, intMode) {
604
+ if (typeof value === "number") {
605
+ if (!Number.isFinite(value)) {
606
+ throw new RangeError("Only finite numbers (not Infinity or NaN) can be passed as arguments");
607
+ }
608
+ return value;
609
+ }
610
+ else if (typeof value === "bigint") {
611
+ if (value < minInteger || value > maxInteger) {
612
+ throw new RangeError("bigint is too large to be represented as a 64-bit integer and passed as argument");
613
+ }
614
+ return value;
615
+ }
616
+ else if (typeof value === "boolean") {
617
+ switch (intMode) {
618
+ case "bigint":
619
+ return value ? 1n : 0n;
620
+ case "string":
621
+ return value ? "1" : "0";
622
+ default:
623
+ return value ? 1 : 0;
624
+ }
625
+ }
626
+ else if (value instanceof ArrayBuffer) {
627
+ return Buffer.from(value);
628
+ }
629
+ else if (value instanceof Date) {
630
+ return value.valueOf();
631
+ }
632
+ else if (value === undefined) {
633
+ throw new TypeError("undefined cannot be passed as argument to the database");
634
+ }
635
+ else {
636
+ return value;
637
+ }
638
+ }
639
+ const minInteger = -9223372036854775808n;
640
+ const maxInteger = 9223372036854775807n;
641
+ function executeMultiple(db, sql) {
642
+ try {
643
+ db.exec(sql);
644
+ }
645
+ catch (e) {
646
+ throw mapSqliteError(e);
647
+ }
648
+ }
649
+ function mapSqliteError(e) {
650
+ if (e instanceof Database.SqliteError) {
651
+ const extendedCode = e.code;
652
+ const code = mapToBaseCode(e.rawCode);
653
+ return new LibsqlError(e.message, code, extendedCode, e.rawCode, e);
654
+ }
655
+ return e;
656
+ }
657
+ // Map SQLite raw error code to base error code string.
658
+ // Extended error codes are (base | (extended << 8)), so base = rawCode & 0xFF
659
+ function mapToBaseCode(rawCode) {
660
+ if (rawCode === undefined) {
661
+ return "SQLITE_UNKNOWN";
662
+ }
663
+ const baseCode = rawCode & 0xff;
664
+ return (sqliteErrorCodes[baseCode] ?? `SQLITE_UNKNOWN_${baseCode.toString()}`);
665
+ }
666
+ const sqliteErrorCodes = {
667
+ 1: "SQLITE_ERROR",
668
+ 2: "SQLITE_INTERNAL",
669
+ 3: "SQLITE_PERM",
670
+ 4: "SQLITE_ABORT",
671
+ 5: "SQLITE_BUSY",
672
+ 6: "SQLITE_LOCKED",
673
+ 7: "SQLITE_NOMEM",
674
+ 8: "SQLITE_READONLY",
675
+ 9: "SQLITE_INTERRUPT",
676
+ 10: "SQLITE_IOERR",
677
+ 11: "SQLITE_CORRUPT",
678
+ 12: "SQLITE_NOTFOUND",
679
+ 13: "SQLITE_FULL",
680
+ 14: "SQLITE_CANTOPEN",
681
+ 15: "SQLITE_PROTOCOL",
682
+ 16: "SQLITE_EMPTY",
683
+ 17: "SQLITE_SCHEMA",
684
+ 18: "SQLITE_TOOBIG",
685
+ 19: "SQLITE_CONSTRAINT",
686
+ 20: "SQLITE_MISMATCH",
687
+ 21: "SQLITE_MISUSE",
688
+ 22: "SQLITE_NOLFS",
689
+ 23: "SQLITE_AUTH",
690
+ 24: "SQLITE_FORMAT",
691
+ 25: "SQLITE_RANGE",
692
+ 26: "SQLITE_NOTADB",
693
+ 27: "SQLITE_NOTICE",
694
+ 28: "SQLITE_WARNING",
695
+ };
@@ -0,0 +1,6 @@
1
+ import type { Config, Client } from "@libsql/core/api";
2
+ import type { ExpandedConfig } from "@libsql/core/config";
3
+ export * from "@libsql/core/api";
4
+ export declare function createClient(config: Config): Client;
5
+ /** @private */
6
+ export declare function _createClient(config: ExpandedConfig): Client;