From 5d8069f80d9d4666ec8cf8e6a2528a53c3cf6552 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Tue, 14 May 2024 08:54:20 -0400 Subject: [PATCH 01/45] WIP: add option to wait for schema changes --- packages/libsql-client/hrana-test-server | 1 + .../src/__tests__/client.test.ts | 17 + packages/libsql-client/src/http.ts | 400 ++++++++++++------ packages/libsql-client/src/ws.ts | 22 +- packages/libsql-core/src/api.ts | 6 +- test.ts | 42 ++ 6 files changed, 350 insertions(+), 138 deletions(-) create mode 160000 packages/libsql-client/hrana-test-server create mode 100644 test.ts diff --git a/packages/libsql-client/hrana-test-server b/packages/libsql-client/hrana-test-server new file mode 160000 index 00000000..e0044a62 --- /dev/null +++ b/packages/libsql-client/hrana-test-server @@ -0,0 +1 @@ +Subproject commit e0044a62f4bd34766197122139c89948e1628be4 diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 6ae29488..ed52c6a4 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -616,6 +616,23 @@ describe("batch()", () => { const rs = await c.execute("SELECT COUNT(*) FROM t"); expect(rs.rows[0][0]).toStrictEqual(0); })); + + test.only("with wait set to true", withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a)"); + + const startTs = Date.now(); + await c.batch([ + "ALTER TABLE t ADD COLUMN phone_number", + "ALTER TABLE t ADD COLUMN another_column" + ], { + transactionMode:"read", + wait: true + }); + + const durationInMs = Date.now() - startTs; + expect(durationInMs).toBeGreaterThanOrEqual(1000); + })); }); describe("transaction()", () => { diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 425812d2..5005c397 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -1,183 +1,315 @@ import * as hrana from "@libsql/hrana-client"; import type { Config, Client } from "@libsql/core/api"; -import type { InStatement, ResultSet, Transaction, IntMode } from "@libsql/core/api"; -import { TransactionMode, LibsqlError } from "@libsql/core/api"; +import type { + InStatement, + ResultSet, + Transaction, + IntMode, +} from "@libsql/core/api"; +import { TransactionMode, BatchConfig, LibsqlError } from "@libsql/core/api"; import type { ExpandedConfig } from "@libsql/core/config"; import { expandConfig } from "@libsql/core/config"; import { - HranaTransaction, executeHranaBatch, - stmtToHrana, resultSetFromHrana, mapHranaError, + HranaTransaction, + executeHranaBatch, + stmtToHrana, + resultSetFromHrana, + mapHranaError, } from "./hrana.js"; import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; +type MigrationResult = { + schema_version: number; + migrations: Array<{ job_id: number; status: string }>; +}; + export * from "@libsql/core/api"; export function createClient(config: Config): Client { - return _createClient(expandConfig(config, true)); + return _createClient(expandConfig(config, true)); } /** @private */ export function _createClient(config: ExpandedConfig): Client { - if (config.scheme !== "https" && config.scheme !== "http") { - throw new LibsqlError( - 'The HTTP client supports only "libsql:", "https:" and "http:" URLs, ' + - `got ${JSON.stringify(config.scheme + ":")}. For more information, please read ${supportedUrlLink}`, - "URL_SCHEME_NOT_SUPPORTED", - ); - } + if (config.scheme !== "https" && config.scheme !== "http") { + throw new LibsqlError( + 'The HTTP client supports only "libsql:", "https:" and "http:" URLs, ' + + `got ${JSON.stringify( + config.scheme + ":" + )}. For more information, please read ${supportedUrlLink}`, + "URL_SCHEME_NOT_SUPPORTED" + ); + } - if (config.encryptionKey !== undefined) { - throw new LibsqlError("Encryption key is not supported by the remote client.", "ENCRYPTION_KEY_NOT_SUPPORTED"); - } + if (config.encryptionKey !== undefined) { + throw new LibsqlError( + "Encryption key is not supported by the remote client.", + "ENCRYPTION_KEY_NOT_SUPPORTED" + ); + } - if (config.scheme === "http" && config.tls) { - throw new LibsqlError(`A "http:" URL cannot opt into TLS by using ?tls=1`, "URL_INVALID"); - } else if (config.scheme === "https" && !config.tls) { - throw new LibsqlError(`A "https:" URL cannot opt out of TLS by using ?tls=0`, "URL_INVALID"); - } + if (config.scheme === "http" && config.tls) { + throw new LibsqlError( + `A "http:" URL cannot opt into TLS by using ?tls=1`, + "URL_INVALID" + ); + } else if (config.scheme === "https" && !config.tls) { + throw new LibsqlError( + `A "https:" URL cannot opt out of TLS by using ?tls=0`, + "URL_INVALID" + ); + } - const url = encodeBaseUrl(config.scheme, config.authority, config.path); - return new HttpClient(url, config.authToken, config.intMode, config.fetch); + const url = encodeBaseUrl(config.scheme, config.authority, config.path); + return new HttpClient(url, config.authToken, config.intMode, config.fetch); } const sqlCacheCapacity = 30; +async function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + export class HttpClient implements Client { - #client: hrana.HttpClient; - protocol: "http"; - - /** @private */ - constructor( - url: URL, - authToken: string | undefined, - intMode: IntMode, - customFetch: Function | undefined, - ) { - this.#client = hrana.openHttp(url, authToken, customFetch); - this.#client.intMode = intMode; - this.protocol = "http"; + #client: hrana.HttpClient; + protocol: "http"; + url: URL; + authToken: string | undefined; + + /** @private */ + constructor( + url: URL, + authToken: string | undefined, + intMode: IntMode, + customFetch: Function | undefined + ) { + this.#client = hrana.openHttp(url, authToken, customFetch); + this.#client.intMode = intMode; + this.protocol = "http"; + this.url = url; + this.authToken = authToken; + } + + async execute(stmt: InStatement): Promise { + try { + const hranaStmt = stmtToHrana(stmt); + + // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the statement and + // close the stream in a single HTTP request. + let rowsPromise: Promise; + const stream = this.#client.openStream(); + try { + rowsPromise = stream.query(hranaStmt); + } finally { + stream.closeGracefully(); + } + + return resultSetFromHrana(await rowsPromise); + } catch (e) { + throw mapHranaError(e); } + } - async execute(stmt: InStatement): Promise { - try { - const hranaStmt = stmtToHrana(stmt); - - // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the statement and - // close the stream in a single HTTP request. - let rowsPromise: Promise; - const stream = this.#client.openStream(); - try { - rowsPromise = stream.query(hranaStmt); - } finally { - stream.closeGracefully(); - } - - return resultSetFromHrana(await rowsPromise); - } catch (e) { - throw mapHranaError(e); - } + async isMigrationJobFinished(jobId: number): Promise { + const url = this.url.origin + `/v1/jobs/${jobId}`; + console.log("isMigrationJobFinished url:", url) + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${this.authToken}`, + }, + }); + const json = (await result.json()); + console.log("json:", json) + const job = json as { status: string }; + if(result.status !== 200) { + throw new Error(`Unexpected status code while fetching job status for migration with id ${jobId}: ${result.status}`); } - async batch(stmts: Array, mode: TransactionMode = "deferred"): Promise> { - try { - const hranaStmts = stmts.map(stmtToHrana); - const version = await this.#client.getVersion(); - - // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and - // close the stream in a single HTTP request. - let resultsPromise: Promise>; - const stream = this.#client.openStream(); - try { - // It makes sense to use a SQL cache even for a single batch, because it may contain the same - // statement repeated multiple times. - const sqlCache = new SqlCache(stream, sqlCacheCapacity); - sqlCache.apply(hranaStmts); - - // TODO: we do not use a cursor here, because it would cause three roundtrips: - // 1. pipeline request to store SQL texts - // 2. cursor request - // 3. pipeline request to close the stream - const batch = stream.batch(false); - resultsPromise = executeHranaBatch(mode, version, batch, hranaStmts); - } finally { - stream.closeGracefully(); - } - - return await resultsPromise; - } catch (e) { - throw mapHranaError(e); - } + if(job.status == "RunFailure") { + throw new Error("Migration job failed"); } - async transaction(mode: TransactionMode = "write"): Promise { - try { - const version = await this.#client.getVersion(); - return new HttpTransaction(this.#client.openStream(), mode, version); - } catch (e) { - throw mapHranaError(e); - } + return job.status == "RunSuccess" + } + + async getLastMigrationJobId(): Promise { + const url = this.url.origin + "/v1/jobs"; + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${this.authToken}`, + }, + }); + if(result.status !== 200) { + throw new Error("Unexpected status code while fetching migration jobs: " + result.status); } - async executeMultiple(sql: string): Promise { - try { - // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the sequence and - // close the stream in a single HTTP request. - let promise: Promise; - const stream = this.#client.openStream(); - try { - promise = stream.sequence(sql); - } finally { - stream.closeGracefully(); - } - - await promise; - } catch (e) { - throw mapHranaError(e); - } + const json = (await result.json()) as MigrationResult; + console.log("json:", json) + if(!json.migrations || json.migrations.length === 0) { + throw new Error("No migrations found"); } - sync(): Promise { - throw new LibsqlError("sync not supported in http mode", "SYNC_NOT_SUPPORTED"); + const migrations = json.migrations || []; + let lastJobId: number | undefined = undefined; + let lastJobStatus: string | undefined = undefined; + for (const migration of migrations) { + if (migration.job_id > (lastJobId || 0)) { + lastJobId = migration.job_id; + lastJobStatus = migration.status; + } + } + if (lastJobStatus === "RunFailure") { + throw new Error("Last migration job failed"); } + if(!lastJobId) { + throw new Error("No migration job found"); + } + + return lastJobId; + } - close(): void { - this.#client.close(); + async batch( + stmts: Array, + mode: TransactionMode | BatchConfig = "deferred" + ): Promise> { + try { + const hranaStmts = stmts.map(stmtToHrana); + const version = await this.#client.getVersion(); + + // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and + // close the stream in a single HTTP request. + let resultsPromise: Promise>; + const stream = this.#client.openStream(); + try { + // It makes sense to use a SQL cache even for a single batch, because it may contain the same + // statement repeated multiple times. + const sqlCache = new SqlCache(stream, sqlCacheCapacity); + sqlCache.apply(hranaStmts); + + // TODO: we do not use a cursor here, because it would cause three roundtrips: + // 1. pipeline request to store SQL texts + // 2. cursor request + // 3. pipeline request to close the stream + const batch = stream.batch(false); + console.log("mode: ", mode); + const transactionMode = + typeof mode === "string" ? mode : mode.transactionMode || "deferred"; + resultsPromise = executeHranaBatch( + transactionMode, + version, + batch, + hranaStmts + ); + } finally { + stream.closeGracefully(); + } + + const lastMigrationJobId = await this.getLastMigrationJobId(); + console.log("lastMigrationJobId: ", lastMigrationJobId); + const wait = typeof mode === "string" ? false : mode.wait; + console.log("wait: ", wait); + if (wait) { + let i = 0 + const SLEEP_TIME_IN_MS = 1 + const MAX_ATTEMPTS = 2 + while(i < MAX_ATTEMPTS) { + i++; + console.log("waiting step", i); + const isLastMigrationJobFinished = await this.isMigrationJobFinished(lastMigrationJobId); + console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) + await sleep(SLEEP_TIME_IN_MS); + } + console.log('Stopped'); + } else { + console.log("not waiting"); + } + + return await resultsPromise; + } catch (e) { + throw mapHranaError(e); + } + } + + async transaction(mode: TransactionMode = "write"): Promise { + try { + const version = await this.#client.getVersion(); + return new HttpTransaction(this.#client.openStream(), mode, version); + } catch (e) { + throw mapHranaError(e); } + } + + async executeMultiple(sql: string): Promise { + try { + // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the sequence and + // close the stream in a single HTTP request. + let promise: Promise; + const stream = this.#client.openStream(); + try { + promise = stream.sequence(sql); + } finally { + stream.closeGracefully(); + } - get closed(): boolean { - return this.#client.closed; + await promise; + } catch (e) { + throw mapHranaError(e); } + } + + sync(): Promise { + throw new LibsqlError( + "sync not supported in http mode", + "SYNC_NOT_SUPPORTED" + ); + } + + close(): void { + this.#client.close(); + } + + get closed(): boolean { + return this.#client.closed; + } } export class HttpTransaction extends HranaTransaction implements Transaction { - #stream: hrana.HttpStream; - #sqlCache: SqlCache; - - /** @private */ - constructor(stream: hrana.HttpStream, mode: TransactionMode, version: hrana.ProtocolVersion) { - super(mode, version); - this.#stream = stream; - this.#sqlCache = new SqlCache(stream, sqlCacheCapacity); - } + #stream: hrana.HttpStream; + #sqlCache: SqlCache; - /** @private */ - override _getStream(): hrana.Stream { - return this.#stream; - } + /** @private */ + constructor( + stream: hrana.HttpStream, + mode: TransactionMode, + version: hrana.ProtocolVersion + ) { + super(mode, version); + this.#stream = stream; + this.#sqlCache = new SqlCache(stream, sqlCacheCapacity); + } - /** @private */ - override _getSqlCache(): SqlCache { - return this.#sqlCache; - } + /** @private */ + override _getStream(): hrana.Stream { + return this.#stream; + } - override close(): void { - this.#stream.close(); - } + /** @private */ + override _getSqlCache(): SqlCache { + return this.#sqlCache; + } - override get closed(): boolean { - return this.#stream.closed; - } + override close(): void { + this.#stream.close(); + } + + override get closed(): boolean { + return this.#stream.closed; + } } diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 5b88c4d3..dca91fdd 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -1,7 +1,7 @@ import * as hrana from "@libsql/hrana-client"; import type { Config, IntMode, Client, Transaction, ResultSet, InStatement } from "@libsql/core/api"; -import { TransactionMode, LibsqlError } from "@libsql/core/api"; +import { TransactionMode, BatchConfig, LibsqlError } from "@libsql/core/api"; import type { ExpandedConfig } from "@libsql/core/config"; import { expandConfig } from "@libsql/core/config"; import { @@ -128,8 +128,15 @@ export class WsClient implements Client { } } - async batch(stmts: Array, mode: TransactionMode = "deferred"): Promise> { + async getLastMigrationJobId(path: string): Promise { + //const response = fetch(path) + //console.log("response: ", response); + return new Promise((resolve) => setTimeout(() => {}, 0)); + } + + async batch(stmts: Array, mode: TransactionMode | BatchConfig): Promise> { const streamState = await this.#openStream(); + console.log("1: ", 1); try { const hranaStmts = stmts.map(stmtToHrana); const version = await streamState.conn.client.getVersion(); @@ -138,7 +145,16 @@ export class WsClient implements Client { // network roundtrip. streamState.conn.sqlCache.apply(hranaStmts); const batch = streamState.stream.batch(version >= 3); - const resultsPromise = executeHranaBatch(mode, version, batch, hranaStmts); + const transactionMode = typeof mode === "string" ? mode : mode.transactionMode || "deferred"; + const resultsPromise = executeHranaBatch(transactionMode, version, batch, hranaStmts); + const wait = typeof mode === "string" ? false : mode.wait; + const path = "/v1/jobs" + const lastMigrationJobId = await this.getLastMigrationJobId(path); + console.log("lastMigrationJobId: ", lastMigrationJobId); + if(wait) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + console.log("1: ", 1); + } return await resultsPromise; } catch (e) { diff --git a/packages/libsql-core/src/api.ts b/packages/libsql-core/src/api.ts index 1424bc7f..37b14d12 100644 --- a/packages/libsql-core/src/api.ts +++ b/packages/libsql-core/src/api.ts @@ -114,7 +114,7 @@ export interface Client { * ], "write"); * ``` */ - batch(stmts: Array, mode?: TransactionMode): Promise>; + batch(stmts: Array, mode?: TransactionMode | BatchConfig): Promise>; /** Start an interactive transaction. * @@ -335,6 +335,10 @@ export interface Transaction { * the previous write transactions to complete. */ export type TransactionMode = "write" | "read" | "deferred"; +export type BatchConfig = { + transactionMode?: TransactionMode; + wait?: boolean; +} /** Result of executing an SQL statement. * diff --git a/test.ts b/test.ts new file mode 100644 index 00000000..9a0e604d --- /dev/null +++ b/test.ts @@ -0,0 +1,42 @@ +const libsqlClient = require("./packages/libsql-client"); +const createClient = libsqlClient.createClient; + +//const client = createClient({ +//url: "libsql://schema-db-test-giovannibenussiparedes.turso.io", +//authToken: +//"eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTQ0ODYzMjAsImlkIjoiMWVjNGY3ZTktNzY3NC00NzEzLWEzYjAtNWM4NzFjNjZlZGQ1In0.lzim2HBCf3W-tnDUwRzeHQz0nbOA_L2D8v_ahfuw_PVq_teuqyFGE0tUgiM_HIvVo6xDdGFQGKj6dFHhLAwrDw", +//}); +const schemaUrl = "libsql://schema-test-giovannibenussi.turso.io"; +const schemaAuthToken = + "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODIwMTAsImlkIjoiM2IyYTIwMDEtOTcxZC00MzIzLWE2YWYtMjk1YTRmOWNkYzVkIn0.l-LzYur2KffpkrZog5vT3eThwB3m2Nl0RIgc5rLn1DpBsYyWujPTkpS62WoYBwWbM0AMaAoRqfyCzi-T-LnJBQ"; + +const client = createClient({ + url: "libsql://schema-child-1-giovannibenussi.turso.io", + authToken: + "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODE0MDIsImlkIjoiYjJhYjRhNjQtNDAyYy00YmRmLWExZTgtMjdlZjMzNTE4Y2JkIn0.Og9E7nl_Y8P93FO1XJlvAhkKEOsGynDdFEziJwLeGrMNaAOhQLqdxk7shao13VQo4JVFkMuSTXMibKXuPnavBA", +}); + +const schemaClient = createClient({ + url: schemaUrl, + authToken: schemaAuthToken, +}); + +async function main() { + await schemaClient.batch(["ALTER TABLE users ADD COLUMN test_column_2 number"], { + wait: true, + }); + + //await client.batch( + //[ + //{ + //sql: "insert into users (first_name) values (?)", + //args: [`Iku ${new Date().toISOString()}`], + //}, + //], + //{ transactionMode: "write", wait: true } + //); + //const users = await client.execute("SELECT * FROM users"); + //console.log("users: ", users.rows); +} + +main(); From 782345b2abf4b22c1f51bd0639f774b9784ddf4d Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 15 May 2024 06:35:54 -0400 Subject: [PATCH 02/45] Wait for schema changes using the API and improve response types --- packages/libsql-client/src/http.ts | 63 ++++++++++++++++++------------ 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 5005c397..9f9dd4b1 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -21,11 +21,27 @@ import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; +type MigrationJobType = { + job_id: number; + status: string; +} + +type ExtendedMigrationJobType = MigrationJobType & { + progress: Array<{ + namespace: string + status: string + error: string | null + }>; +}; + type MigrationResult = { schema_version: number; - migrations: Array<{ job_id: number; status: string }>; + migrations: Array; }; +const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1 +const SCHEMA_MIGRATION_MAX_RETRIES = 2 + export * from "@libsql/core/api"; export function createClient(config: Config): Client { @@ -124,7 +140,7 @@ export class HttpClient implements Client { Authorization: `Bearer ${this.authToken}`, }, }); - const json = (await result.json()); + const json = (await result.json()) as ExtendedMigrationJobType; console.log("json:", json) const job = json as { status: string }; if(result.status !== 200) { @@ -138,7 +154,7 @@ export class HttpClient implements Client { return job.status == "RunSuccess" } - async getLastMigrationJobId(): Promise { + async getLastMigrationJob(): Promise { const url = this.url.origin + "/v1/jobs"; const result = await fetch(url, { method: "GET", @@ -157,22 +173,20 @@ export class HttpClient implements Client { } const migrations = json.migrations || []; - let lastJobId: number | undefined = undefined; - let lastJobStatus: string | undefined = undefined; + let lastJob: MigrationJobType | undefined; for (const migration of migrations) { - if (migration.job_id > (lastJobId || 0)) { - lastJobId = migration.job_id; - lastJobStatus = migration.status; + if (migration.job_id > (lastJob?.job_id || 0)) { + lastJob = migration; } } - if (lastJobStatus === "RunFailure") { - throw new Error("Last migration job failed"); - } - if(!lastJobId) { + if(!lastJob) { throw new Error("No migration job found"); } + if (lastJob?.status === "RunFailure") { + throw new Error("Last migration job failed"); + } - return lastJobId; + return lastJob; } async batch( @@ -211,20 +225,19 @@ export class HttpClient implements Client { stream.closeGracefully(); } - const lastMigrationJobId = await this.getLastMigrationJobId(); - console.log("lastMigrationJobId: ", lastMigrationJobId); const wait = typeof mode === "string" ? false : mode.wait; - console.log("wait: ", wait); if (wait) { - let i = 0 - const SLEEP_TIME_IN_MS = 1 - const MAX_ATTEMPTS = 2 - while(i < MAX_ATTEMPTS) { - i++; - console.log("waiting step", i); - const isLastMigrationJobFinished = await this.isMigrationJobFinished(lastMigrationJobId); - console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) - await sleep(SLEEP_TIME_IN_MS); + const lastMigrationJob = await this.getLastMigrationJob(); + console.log("lastMigrationJob:", lastMigrationJob) + if(lastMigrationJob.status !== "RunSuccess") { + let i = 0 + while(i < SCHEMA_MIGRATION_MAX_RETRIES) { + i++; + console.log("waiting step", i); + const isLastMigrationJobFinished = await this.isMigrationJobFinished(lastMigrationJob.job_id); + console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) + await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); + } } console.log('Stopped'); } else { From bc9680619d9a7b10c9326271c002b24ccf64c9e6 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 15 May 2024 07:08:35 -0400 Subject: [PATCH 03/45] Undo format changes from http module --- packages/libsql-client/src/http.ts | 491 ++++++++++++++--------------- 1 file changed, 229 insertions(+), 262 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 9f9dd4b1..28301f6c 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -1,21 +1,13 @@ import * as hrana from "@libsql/hrana-client"; import type { Config, Client } from "@libsql/core/api"; -import type { - InStatement, - ResultSet, - Transaction, - IntMode, -} from "@libsql/core/api"; -import { TransactionMode, BatchConfig, LibsqlError } from "@libsql/core/api"; +import type { BatchConfig, InStatement, ResultSet, Transaction, IntMode } from "@libsql/core/api"; +import { TransactionMode, LibsqlError } from "@libsql/core/api"; import type { ExpandedConfig } from "@libsql/core/config"; import { expandConfig } from "@libsql/core/config"; import { - HranaTransaction, - executeHranaBatch, - stmtToHrana, - resultSetFromHrana, - mapHranaError, + HranaTransaction, executeHranaBatch, + stmtToHrana, resultSetFromHrana, mapHranaError, } from "./hrana.js"; import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; @@ -42,287 +34,262 @@ type MigrationResult = { const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1 const SCHEMA_MIGRATION_MAX_RETRIES = 2 +async function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + export * from "@libsql/core/api"; export function createClient(config: Config): Client { - return _createClient(expandConfig(config, true)); + return _createClient(expandConfig(config, true)); } /** @private */ export function _createClient(config: ExpandedConfig): Client { - if (config.scheme !== "https" && config.scheme !== "http") { - throw new LibsqlError( - 'The HTTP client supports only "libsql:", "https:" and "http:" URLs, ' + - `got ${JSON.stringify( - config.scheme + ":" - )}. For more information, please read ${supportedUrlLink}`, - "URL_SCHEME_NOT_SUPPORTED" - ); - } - - if (config.encryptionKey !== undefined) { - throw new LibsqlError( - "Encryption key is not supported by the remote client.", - "ENCRYPTION_KEY_NOT_SUPPORTED" - ); - } - - if (config.scheme === "http" && config.tls) { - throw new LibsqlError( - `A "http:" URL cannot opt into TLS by using ?tls=1`, - "URL_INVALID" - ); - } else if (config.scheme === "https" && !config.tls) { - throw new LibsqlError( - `A "https:" URL cannot opt out of TLS by using ?tls=0`, - "URL_INVALID" - ); - } - - const url = encodeBaseUrl(config.scheme, config.authority, config.path); - return new HttpClient(url, config.authToken, config.intMode, config.fetch); -} + if (config.scheme !== "https" && config.scheme !== "http") { + throw new LibsqlError( + 'The HTTP client supports only "libsql:", "https:" and "http:" URLs, ' + + `got ${JSON.stringify(config.scheme + ":")}. For more information, please read ${supportedUrlLink}`, + "URL_SCHEME_NOT_SUPPORTED", + ); + } -const sqlCacheCapacity = 30; + if (config.encryptionKey !== undefined) { + throw new LibsqlError("Encryption key is not supported by the remote client.", "ENCRYPTION_KEY_NOT_SUPPORTED"); + } -async function sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); + if (config.scheme === "http" && config.tls) { + throw new LibsqlError(`A "http:" URL cannot opt into TLS by using ?tls=1`, "URL_INVALID"); + } else if (config.scheme === "https" && !config.tls) { + throw new LibsqlError(`A "https:" URL cannot opt out of TLS by using ?tls=0`, "URL_INVALID"); + } + + const url = encodeBaseUrl(config.scheme, config.authority, config.path); + return new HttpClient(url, config.authToken, config.intMode, config.fetch); } -export class HttpClient implements Client { - #client: hrana.HttpClient; - protocol: "http"; - url: URL; - authToken: string | undefined; - - /** @private */ - constructor( - url: URL, - authToken: string | undefined, - intMode: IntMode, - customFetch: Function | undefined - ) { - this.#client = hrana.openHttp(url, authToken, customFetch); - this.#client.intMode = intMode; - this.protocol = "http"; - this.url = url; - this.authToken = authToken; - } - - async execute(stmt: InStatement): Promise { - try { - const hranaStmt = stmtToHrana(stmt); - - // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the statement and - // close the stream in a single HTTP request. - let rowsPromise: Promise; - const stream = this.#client.openStream(); - try { - rowsPromise = stream.query(hranaStmt); - } finally { - stream.closeGracefully(); - } +const sqlCacheCapacity = 30; - return resultSetFromHrana(await rowsPromise); - } catch (e) { - throw mapHranaError(e); - } - } - - async isMigrationJobFinished(jobId: number): Promise { - const url = this.url.origin + `/v1/jobs/${jobId}`; - console.log("isMigrationJobFinished url:", url) - const result = await fetch(url, { - method: "GET", - headers: { - Authorization: `Bearer ${this.authToken}`, - }, - }); - const json = (await result.json()) as ExtendedMigrationJobType; - console.log("json:", json) - const job = json as { status: string }; - if(result.status !== 200) { - throw new Error(`Unexpected status code while fetching job status for migration with id ${jobId}: ${result.status}`); +export class HttpClient implements Client { + #client: hrana.HttpClient; + protocol: "http"; + url: URL; + authToken: string | undefined; + + /** @private */ + constructor( + url: URL, + authToken: string | undefined, + intMode: IntMode, + customFetch: Function | undefined, + ) { + this.#client = hrana.openHttp(url, authToken, customFetch); + this.#client.intMode = intMode; + this.protocol = "http"; + this.url = url; + this.authToken = authToken; } - if(job.status == "RunFailure") { - throw new Error("Migration job failed"); + async execute(stmt: InStatement): Promise { + try { + const hranaStmt = stmtToHrana(stmt); + + // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the statement and + // close the stream in a single HTTP request. + let rowsPromise: Promise; + const stream = this.#client.openStream(); + try { + rowsPromise = stream.query(hranaStmt); + } finally { + stream.closeGracefully(); + } + + return resultSetFromHrana(await rowsPromise); + } catch (e) { + throw mapHranaError(e); + } } - return job.status == "RunSuccess" - } - - async getLastMigrationJob(): Promise { - const url = this.url.origin + "/v1/jobs"; - const result = await fetch(url, { - method: "GET", - headers: { - Authorization: `Bearer ${this.authToken}`, - }, - }); - if(result.status !== 200) { - throw new Error("Unexpected status code while fetching migration jobs: " + result.status); - } + async isMigrationJobFinished(jobId: number): Promise { + const url = this.url.origin + `/v1/jobs/${jobId}`; + console.log("isMigrationJobFinished url:", url) + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${this.authToken}`, + }, + }); + const json = (await result.json()) as ExtendedMigrationJobType; + console.log("json:", json) + const job = json as { status: string }; + if(result.status !== 200) { + throw new Error(`Unexpected status code while fetching job status for migration with id ${jobId}: ${result.status}`); + } - const json = (await result.json()) as MigrationResult; - console.log("json:", json) - if(!json.migrations || json.migrations.length === 0) { - throw new Error("No migrations found"); + if(job.status == "RunFailure") { + throw new Error("Migration job failed"); + } + + return job.status == "RunSuccess" } - const migrations = json.migrations || []; - let lastJob: MigrationJobType | undefined; - for (const migration of migrations) { - if (migration.job_id > (lastJob?.job_id || 0)) { - lastJob = migration; + async getLastMigrationJob(): Promise { + const url = this.url.origin + "/v1/jobs"; + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${this.authToken}`, + }, + }); + if(result.status !== 200) { + throw new Error("Unexpected status code while fetching migration jobs: " + result.status); } - } - if(!lastJob) { - throw new Error("No migration job found"); - } - if (lastJob?.status === "RunFailure") { - throw new Error("Last migration job failed"); - } - return lastJob; - } - - async batch( - stmts: Array, - mode: TransactionMode | BatchConfig = "deferred" - ): Promise> { - try { - const hranaStmts = stmts.map(stmtToHrana); - const version = await this.#client.getVersion(); - - // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and - // close the stream in a single HTTP request. - let resultsPromise: Promise>; - const stream = this.#client.openStream(); - try { - // It makes sense to use a SQL cache even for a single batch, because it may contain the same - // statement repeated multiple times. - const sqlCache = new SqlCache(stream, sqlCacheCapacity); - sqlCache.apply(hranaStmts); - - // TODO: we do not use a cursor here, because it would cause three roundtrips: - // 1. pipeline request to store SQL texts - // 2. cursor request - // 3. pipeline request to close the stream - const batch = stream.batch(false); - console.log("mode: ", mode); - const transactionMode = - typeof mode === "string" ? mode : mode.transactionMode || "deferred"; - resultsPromise = executeHranaBatch( - transactionMode, - version, - batch, - hranaStmts - ); - } finally { - stream.closeGracefully(); + const json = (await result.json()) as MigrationResult; + console.log("json:", json) + if(!json.migrations || json.migrations.length === 0) { + throw new Error("No migrations found"); } - const wait = typeof mode === "string" ? false : mode.wait; - if (wait) { - const lastMigrationJob = await this.getLastMigrationJob(); - console.log("lastMigrationJob:", lastMigrationJob) - if(lastMigrationJob.status !== "RunSuccess") { - let i = 0 - while(i < SCHEMA_MIGRATION_MAX_RETRIES) { - i++; - console.log("waiting step", i); - const isLastMigrationJobFinished = await this.isMigrationJobFinished(lastMigrationJob.job_id); - console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) - await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); - } + const migrations = json.migrations || []; + let lastJob: MigrationJobType | undefined; + for (const migration of migrations) { + if (migration.job_id > (lastJob?.job_id || 0)) { + lastJob = migration; } - console.log('Stopped'); - } else { - console.log("not waiting"); } + if(!lastJob) { + throw new Error("No migration job found"); + } + if (lastJob?.status === "RunFailure") { + throw new Error("Last migration job failed"); + } + + return lastJob; + } - return await resultsPromise; - } catch (e) { - throw mapHranaError(e); + async batch( + stmts: Array, + mode: TransactionMode | BatchConfig = "deferred" + ): Promise> { + try { + const hranaStmts = stmts.map(stmtToHrana); + const version = await this.#client.getVersion(); + + // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and + // close the stream in a single HTTP request. + let resultsPromise: Promise>; + const stream = this.#client.openStream(); + try { + // It makes sense to use a SQL cache even for a single batch, because it may contain the same + // statement repeated multiple times. + const sqlCache = new SqlCache(stream, sqlCacheCapacity); + sqlCache.apply(hranaStmts); + + // TODO: we do not use a cursor here, because it would cause three roundtrips: + // 1. pipeline request to store SQL texts + // 2. cursor request + // 3. pipeline request to close the stream + const batch = stream.batch(false); + const transactionMode = typeof mode === "string" ? mode : mode.transactionMode || "deferred"; + resultsPromise = executeHranaBatch(transactionMode, version, batch, hranaStmts); + } finally { + stream.closeGracefully(); + } + + const wait = typeof mode === "string" ? false : mode.wait; + if (wait) { + const lastMigrationJob = await this.getLastMigrationJob(); + console.log("lastMigrationJob:", lastMigrationJob) + if(lastMigrationJob.status !== "RunSuccess") { + let i = 0 + while(i < SCHEMA_MIGRATION_MAX_RETRIES) { + i++; + console.log("waiting step", i); + const isLastMigrationJobFinished = await this.isMigrationJobFinished(lastMigrationJob.job_id); + console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) + await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); + } + } + console.log('Stopped'); + } else { + console.log("not waiting"); + } + + return await resultsPromise; + } catch (e) { + throw mapHranaError(e); + } } - } - - async transaction(mode: TransactionMode = "write"): Promise { - try { - const version = await this.#client.getVersion(); - return new HttpTransaction(this.#client.openStream(), mode, version); - } catch (e) { - throw mapHranaError(e); + + async transaction(mode: TransactionMode = "write"): Promise { + try { + const version = await this.#client.getVersion(); + return new HttpTransaction(this.#client.openStream(), mode, version); + } catch (e) { + throw mapHranaError(e); + } + } + + async executeMultiple(sql: string): Promise { + try { + // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the sequence and + // close the stream in a single HTTP request. + let promise: Promise; + const stream = this.#client.openStream(); + try { + promise = stream.sequence(sql); + } finally { + stream.closeGracefully(); + } + + await promise; + } catch (e) { + throw mapHranaError(e); + } } - } - - async executeMultiple(sql: string): Promise { - try { - // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the sequence and - // close the stream in a single HTTP request. - let promise: Promise; - const stream = this.#client.openStream(); - try { - promise = stream.sequence(sql); - } finally { - stream.closeGracefully(); - } - await promise; - } catch (e) { - throw mapHranaError(e); + sync(): Promise { + throw new LibsqlError("sync not supported in http mode", "SYNC_NOT_SUPPORTED"); + } + + close(): void { + this.#client.close(); + } + + get closed(): boolean { + return this.#client.closed; } - } - - sync(): Promise { - throw new LibsqlError( - "sync not supported in http mode", - "SYNC_NOT_SUPPORTED" - ); - } - - close(): void { - this.#client.close(); - } - - get closed(): boolean { - return this.#client.closed; - } } export class HttpTransaction extends HranaTransaction implements Transaction { - #stream: hrana.HttpStream; - #sqlCache: SqlCache; - - /** @private */ - constructor( - stream: hrana.HttpStream, - mode: TransactionMode, - version: hrana.ProtocolVersion - ) { - super(mode, version); - this.#stream = stream; - this.#sqlCache = new SqlCache(stream, sqlCacheCapacity); - } - - /** @private */ - override _getStream(): hrana.Stream { - return this.#stream; - } - - /** @private */ - override _getSqlCache(): SqlCache { - return this.#sqlCache; - } - - override close(): void { - this.#stream.close(); - } - - override get closed(): boolean { - return this.#stream.closed; - } + #stream: hrana.HttpStream; + #sqlCache: SqlCache; + + /** @private */ + constructor(stream: hrana.HttpStream, mode: TransactionMode, version: hrana.ProtocolVersion) { + super(mode, version); + this.#stream = stream; + this.#sqlCache = new SqlCache(stream, sqlCacheCapacity); + } + + /** @private */ + override _getStream(): hrana.Stream { + return this.#stream; + } + + /** @private */ + override _getSqlCache(): SqlCache { + return this.#sqlCache; + } + + override close(): void { + this.#stream.close(); + } + + override get closed(): boolean { + return this.#stream.closed; + } } From 9c70b8f5763c78b21f4910770431fc723c6fac3d Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 15 May 2024 07:24:40 -0400 Subject: [PATCH 04/45] Update logs while waiting for migration jobs --- packages/libsql-client/src/http.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 28301f6c..544db8ed 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -201,21 +201,22 @@ export class HttpClient implements Client { const wait = typeof mode === "string" ? false : mode.wait; if (wait) { + console.log('Waiting for migration jobs'); const lastMigrationJob = await this.getLastMigrationJob(); console.log("lastMigrationJob:", lastMigrationJob) if(lastMigrationJob.status !== "RunSuccess") { let i = 0 while(i < SCHEMA_MIGRATION_MAX_RETRIES) { i++; - console.log("waiting step", i); + console.log("Waiting for migration job to finish, attempt:", i); const isLastMigrationJobFinished = await this.isMigrationJobFinished(lastMigrationJob.job_id); console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); } } - console.log('Stopped'); + console.log('Finished waiting for migration jobs'); } else { - console.log("not waiting"); + console.log("Not waiting for migration jobs"); } return await resultsPromise; From 247a1d487d4c3dcc5dbab7e0277bb83024dbf8f2 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 15 May 2024 07:46:21 -0400 Subject: [PATCH 05/45] Remove unused code from ws module --- packages/libsql-client/src/ws.ts | 16 ---------------- test.ts | 5 +++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index dca91fdd..2886767c 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -128,15 +128,8 @@ export class WsClient implements Client { } } - async getLastMigrationJobId(path: string): Promise { - //const response = fetch(path) - //console.log("response: ", response); - return new Promise((resolve) => setTimeout(() => {}, 0)); - } - async batch(stmts: Array, mode: TransactionMode | BatchConfig): Promise> { const streamState = await this.#openStream(); - console.log("1: ", 1); try { const hranaStmts = stmts.map(stmtToHrana); const version = await streamState.conn.client.getVersion(); @@ -147,15 +140,6 @@ export class WsClient implements Client { const batch = streamState.stream.batch(version >= 3); const transactionMode = typeof mode === "string" ? mode : mode.transactionMode || "deferred"; const resultsPromise = executeHranaBatch(transactionMode, version, batch, hranaStmts); - const wait = typeof mode === "string" ? false : mode.wait; - const path = "/v1/jobs" - const lastMigrationJobId = await this.getLastMigrationJobId(path); - console.log("lastMigrationJobId: ", lastMigrationJobId); - if(wait) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - console.log("1: ", 1); - } - return await resultsPromise; } catch (e) { throw mapHranaError(e); diff --git a/test.ts b/test.ts index 9a0e604d..34e7e24e 100644 --- a/test.ts +++ b/test.ts @@ -22,8 +22,9 @@ const schemaClient = createClient({ }); async function main() { - await schemaClient.batch(["ALTER TABLE users ADD COLUMN test_column_2 number"], { - wait: true, + //await schemaClient.execute("ALTER TABLE users ADD COLUMN test_column_5 number"); + await schemaClient.batch(["ALTER TABLE users ADD COLUMN test_column_5 number"], { + wait: false, }); //await client.batch( From eef5fc33fa2e3d8c1c2803a7835d7b69abdf72f7 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 15 May 2024 07:46:43 -0400 Subject: [PATCH 06/45] Provide default vale for batch on the ws module --- packages/libsql-client/src/ws.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 2886767c..a785aa4c 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -128,7 +128,7 @@ export class WsClient implements Client { } } - async batch(stmts: Array, mode: TransactionMode | BatchConfig): Promise> { + async batch(stmts: Array, mode: TransactionMode | BatchConfig = "deferred"): Promise> { const streamState = await this.#openStream(); try { const hranaStmts = stmts.map(stmtToHrana); @@ -138,7 +138,7 @@ export class WsClient implements Client { // network roundtrip. streamState.conn.sqlCache.apply(hranaStmts); const batch = streamState.stream.batch(version >= 3); - const transactionMode = typeof mode === "string" ? mode : mode.transactionMode || "deferred"; + const transactionMode = (typeof mode === "string" ? mode : mode.transactionMode) || "deferred"; const resultsPromise = executeHranaBatch(transactionMode, version, batch, hranaStmts); return await resultsPromise; } catch (e) { From 4853f9491b172bd7971a5461f099fd3dd44e5eec Mon Sep 17 00:00:00 2001 From: Giovanni Date: Thu, 16 May 2024 07:23:11 -0400 Subject: [PATCH 07/45] Extract waitForLastMigrationJobToFinish to its own module --- packages/libsql-client/src/http.ts | 103 ++--------------------------- test.ts | 4 +- 2 files changed, 7 insertions(+), 100 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 544db8ed..4e3a8e56 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -12,33 +12,7 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; - -type MigrationJobType = { - job_id: number; - status: string; -} - -type ExtendedMigrationJobType = MigrationJobType & { - progress: Array<{ - namespace: string - status: string - error: string | null - }>; -}; - -type MigrationResult = { - schema_version: number; - migrations: Array; -}; - -const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1 -const SCHEMA_MIGRATION_MAX_RETRIES = 2 - -async function sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} +import { waitForLastMigrationJobToFinish } from "./migrations"; export * from "@libsql/core/api"; @@ -112,64 +86,6 @@ export class HttpClient implements Client { } } - async isMigrationJobFinished(jobId: number): Promise { - const url = this.url.origin + `/v1/jobs/${jobId}`; - console.log("isMigrationJobFinished url:", url) - const result = await fetch(url, { - method: "GET", - headers: { - Authorization: `Bearer ${this.authToken}`, - }, - }); - const json = (await result.json()) as ExtendedMigrationJobType; - console.log("json:", json) - const job = json as { status: string }; - if(result.status !== 200) { - throw new Error(`Unexpected status code while fetching job status for migration with id ${jobId}: ${result.status}`); - } - - if(job.status == "RunFailure") { - throw new Error("Migration job failed"); - } - - return job.status == "RunSuccess" - } - - async getLastMigrationJob(): Promise { - const url = this.url.origin + "/v1/jobs"; - const result = await fetch(url, { - method: "GET", - headers: { - Authorization: `Bearer ${this.authToken}`, - }, - }); - if(result.status !== 200) { - throw new Error("Unexpected status code while fetching migration jobs: " + result.status); - } - - const json = (await result.json()) as MigrationResult; - console.log("json:", json) - if(!json.migrations || json.migrations.length === 0) { - throw new Error("No migrations found"); - } - - const migrations = json.migrations || []; - let lastJob: MigrationJobType | undefined; - for (const migration of migrations) { - if (migration.job_id > (lastJob?.job_id || 0)) { - lastJob = migration; - } - } - if(!lastJob) { - throw new Error("No migration job found"); - } - if (lastJob?.status === "RunFailure") { - throw new Error("Last migration job failed"); - } - - return lastJob; - } - async batch( stmts: Array, mode: TransactionMode | BatchConfig = "deferred" @@ -201,19 +117,10 @@ export class HttpClient implements Client { const wait = typeof mode === "string" ? false : mode.wait; if (wait) { - console.log('Waiting for migration jobs'); - const lastMigrationJob = await this.getLastMigrationJob(); - console.log("lastMigrationJob:", lastMigrationJob) - if(lastMigrationJob.status !== "RunSuccess") { - let i = 0 - while(i < SCHEMA_MIGRATION_MAX_RETRIES) { - i++; - console.log("Waiting for migration job to finish, attempt:", i); - const isLastMigrationJobFinished = await this.isMigrationJobFinished(lastMigrationJob.job_id); - console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) - await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); - } - } + await waitForLastMigrationJobToFinish({ + authToken: this.authToken, + baseUrl: this.url.origin, + }) console.log('Finished waiting for migration jobs'); } else { console.log("Not waiting for migration jobs"); diff --git a/test.ts b/test.ts index 34e7e24e..e4e1975f 100644 --- a/test.ts +++ b/test.ts @@ -23,8 +23,8 @@ const schemaClient = createClient({ async function main() { //await schemaClient.execute("ALTER TABLE users ADD COLUMN test_column_5 number"); - await schemaClient.batch(["ALTER TABLE users ADD COLUMN test_column_5 number"], { - wait: false, + await schemaClient.batch(["ALTER TABLE users ADD COLUMN test_column_6 number;"], { + wait: true, }); //await client.batch( From af0920588fab7b2bdd8e1d72e0e4d4e71c906658 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Thu, 16 May 2024 07:31:30 -0400 Subject: [PATCH 08/45] Extract waitForLastMigrationJobToFinish to its own module --- packages/libsql-client/src/migrations.ts | 133 +++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 packages/libsql-client/src/migrations.ts diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts new file mode 100644 index 00000000..1f6b911d --- /dev/null +++ b/packages/libsql-client/src/migrations.ts @@ -0,0 +1,133 @@ +type MigrationJobType = { + job_id: number; + status: string; +} + +type ExtendedMigrationJobType = MigrationJobType & { + progress: Array<{ + namespace: string + status: string + error: string | null + }>; +}; + +type MigrationResult = { + schema_version: number; + migrations: Array; +}; + +const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1 +const SCHEMA_MIGRATION_MAX_RETRIES = 2 + +async function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +type isMigrationJobFinishedProps = { + authToken: string | undefined + baseUrl: string + jobId: number +} + +async function isMigrationJobFinished({ + authToken, + baseUrl, + jobId +}:isMigrationJobFinishedProps): Promise { + const url = baseUrl + `/v1/jobs/${jobId}`; + console.log("isMigrationJobFinished url:", url) + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + const json = (await result.json()) as ExtendedMigrationJobType; + console.log("json:", json) + const job = json as { status: string }; + if(result.status !== 200) { + throw new Error(`Unexpected status code while fetching job status for migration with id ${jobId}: ${result.status}`); + } + + if(job.status == "RunFailure") { + throw new Error("Migration job failed"); + } + +return job.status == "RunSuccess" +} + +type getLastMigrationJobProps = { + authToken: string | undefined + baseUrl: string +} + +async function getLastMigrationJob({ + authToken, + baseUrl +}:getLastMigrationJobProps): Promise { + const url = baseUrl + "/v1/jobs"; + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + if(result.status !== 200) { + throw new Error("Unexpected status code while fetching migration jobs: " + result.status); + } + + const json = (await result.json()) as MigrationResult; + console.log("json:", json) + if(!json.migrations || json.migrations.length === 0) { + throw new Error("No migrations found"); + } + + const migrations = json.migrations || []; + let lastJob: MigrationJobType | undefined; + for (const migration of migrations) { + if (migration.job_id > (lastJob?.job_id || 0)) { + lastJob = migration; + } + } + if(!lastJob) { + throw new Error("No migration job found"); + } + if (lastJob?.status === "RunFailure") { + throw new Error("Last migration job failed"); + } + + return lastJob; +} + +type waitForLastMigrationJobToFinishProps = { + authToken: string | undefined + baseUrl: string +} + +export async function waitForLastMigrationJobToFinish({ + authToken, + baseUrl +}:getLastMigrationJobProps) { + console.log('Waiting for migration jobs'); + const lastMigrationJob = await getLastMigrationJob({ + authToken: authToken, + baseUrl + }); + console.log("lastMigrationJob:", lastMigrationJob) + if(lastMigrationJob.status !== "RunSuccess") { + let i = 0 + while(i < SCHEMA_MIGRATION_MAX_RETRIES) { + i++; + console.log("Waiting for migration job to finish, attempt:", i); + const isLastMigrationJobFinished = await isMigrationJobFinished({ + authToken: authToken, + baseUrl, + jobId: lastMigrationJob.job_id + }) + console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) + await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); + } + } +} From 8a568be4bfbc47db14b4b1b894939ff4a8103002 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 14:31:48 -0400 Subject: [PATCH 09/45] WIP: Add MSW and merge main --- package-lock.json | 471 +++- packages/libsql-client/package.json | 1 + .../src/__tests__/client.test.ts | 2018 ++++++++++------- .../libsql-client/src/__tests__/handlers.ts | 16 + packages/libsql-client/src/__tests__/mocks.ts | 7 + 5 files changed, 1556 insertions(+), 957 deletions(-) create mode 100644 packages/libsql-client/src/__tests__/handlers.ts create mode 100644 packages/libsql-client/src/__tests__/mocks.ts diff --git a/package-lock.json b/package-lock.json index 91735f44..41b67498 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,6 +597,105 @@ "dev": true, "license": "MIT" }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@inquirer/confirm": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^8.2.0", + "@inquirer/type": "^1.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.1", + "@inquirer/type": "^1.3.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^20.12.11", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "20.12.12", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -941,23 +1040,10 @@ }, "node_modules/@libsql/darwin-arm64": { "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.3.10.tgz", - "integrity": "sha512-RaexEFfPAFogd6dJlqkpCkTxdr6K14Z0286lodIJ8Ny77mWuWyBkWKxf70OYWXXAMxMJFUW+6al1F3/Osf/pTg==", "cpu": [ "arm64" ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@libsql/darwin-x64": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.3.10.tgz", - "integrity": "sha512-SNVN6n4qNUdMW1fJMFmx4qn4n5RnXsxjFbczpkzG/V7m/5VeTFt1chhGcrahTHCr3+K6eRJWJUEQHRGqjBwPkw==", - "cpu": [ - "x64" - ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -965,8 +1051,7 @@ }, "node_modules/@libsql/hrana-client": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.6.0.tgz", - "integrity": "sha512-k+fqzdjqg3IvWfKmVJK5StsbjeTcyNAXFelUbXbGNz3yH1gEVT9mZ6kmhsIXP30ZSyVV0AE1Gi25p82mxC9hwg==", + "license": "MIT", "dependencies": { "@libsql/isomorphic-fetch": "^0.2.1", "@libsql/isomorphic-ws": "^0.1.5", @@ -976,8 +1061,7 @@ }, "node_modules/@libsql/isomorphic-fetch": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.2.1.tgz", - "integrity": "sha512-Sv07QP1Aw8A5OOrmKgRUBKe2fFhF2hpGJhtHe3d1aRnTESZCGkn//0zDycMKTGamVWb3oLYRroOsCV8Ukes9GA==" + "license": "MIT" }, "node_modules/@libsql/isomorphic-ws": { "version": "0.1.5", @@ -989,76 +1073,57 @@ }, "node_modules/@libsql/libsql-wasm-experimental": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@libsql/libsql-wasm-experimental/-/libsql-wasm-experimental-0.0.2.tgz", - "integrity": "sha512-xkiu88QwozGr3KEt9h0zeHLYUIWkeDchXmuOUW4/Wh/mRZkDlNtIIePAR0FiLl1j0o4OyTEOtPnvmaXQ5MNTKQ==", + "license": "Apache-2.0", "bin": { "sqlite-wasm": "bin/index.js" } }, - "node_modules/@libsql/linux-arm64-gnu": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.3.10.tgz", - "integrity": "sha512-2uXpi9d8qtyIOr7pyG4a88j6YXgemyIHEs2Wbp+PPletlCIPsFS+E7IQHbz8VwTohchOzcokGUm1Bc5QC+A7wg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@mswjs/cookies": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, - "node_modules/@libsql/linux-arm64-musl": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.3.10.tgz", - "integrity": "sha512-72SN1FUavLvzHddCS861ynSpQndcW5oLGKA3U8CyMfgIZIwJAPc7+48Uj1plW00htXBx4GBpcntFp68KKIx3YQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@mswjs/interceptors": { + "version": "0.29.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/@libsql/linux-x64-gnu": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.3.10.tgz", - "integrity": "sha512-hXyNqVRi7ONuyWZ1SX6setxL0QaQ7InyS3bHLupsi9s7NpOGD5vcpTaYicJOqmIIm+6kt8vJfmo7ZxlarIHy7Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@neon-rs/load": { + "version": "0.0.4", + "license": "MIT" }, - "node_modules/@libsql/linux-x64-musl": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.3.10.tgz", - "integrity": "sha512-kNmIRxomVwt9S+cLyYS497F/3gXFF4r8wW12YSBQgxG75JYft07AHVd8J7HINg+oqRkLzT0s+mVX5dM6nk68EQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "dev": true, + "license": "MIT" }, - "node_modules/@libsql/win32-x64-msvc": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.3.10.tgz", - "integrity": "sha512-c/6rjdtGULKrJkLgfLobFefObfOtxjXGmCfPxv6pr0epPCeUEssfDbDIeEH9fQUgzogIMWEHwT8so52UJ/iT1Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ] + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } }, - "node_modules/@neon-rs/load": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", - "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==" + "node_modules/@open-draft/until": { + "version": "2.1.0", + "dev": true, + "license": "MIT" }, "node_modules/@sinclair/typebox": { "version": "0.27.8", @@ -1118,6 +1183,11 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "dev": true, + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "dev": true, @@ -1156,6 +1226,14 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "18.19.8", "license": "MIT", @@ -1168,6 +1246,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.5.10", "license": "MIT", @@ -1365,10 +1453,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "license": "MIT", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1520,6 +1609,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -1581,6 +1681,14 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, "node_modules/cliui": { "version": "8.0.1", "dev": true, @@ -1630,11 +1738,11 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/concat-map": { @@ -1647,6 +1755,14 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/create-jest": { "version": "29.7.0", "dev": true, @@ -1724,8 +1840,7 @@ }, "node_modules/detect-libc": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "license": "Apache-2.0", "engines": { "node": ">=8" } @@ -1887,8 +2002,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "license": "MIT", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2009,6 +2125,14 @@ "dev": true, "license": "ISC" }, + "node_modules/graphql": { + "version": "16.8.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -2028,6 +2152,11 @@ "node": ">= 0.4" } }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "dev": true, + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.2", "dev": true, @@ -2127,9 +2256,15 @@ "node": ">=6" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "engines": { "node": ">=0.12.0" } @@ -2868,13 +3003,12 @@ }, "node_modules/libsql": { "version": "0.3.10", - "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.3.10.tgz", - "integrity": "sha512-/8YMTbwWFPmrDWY+YFK3kYqVPFkMgQre0DGmBaOmjogMdSe+7GHm1/q9AZ61AWkEub/vHmi+bA4tqIzVhKnqzg==", "cpu": [ "x64", "arm64", "wasm32" ], + "license": "MIT", "os": [ "darwin", "linux", @@ -2895,11 +3029,14 @@ } }, "node_modules/lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -2908,20 +3045,20 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz", - "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.4.tgz", + "integrity": "sha512-3F9KRQIS2fVDGtCkBp4Bx0jswjX7zUcKx6OF0ZeY1prksUyKPRIIUqZhIUYAstJfvj6i48VFs4dwVIbCYwvTYQ==", "dependencies": { "chalk": "5.3.0", - "commander": "11.1.0", + "commander": "12.1.0", "debug": "4.3.4", "execa": "8.0.1", - "lilconfig": "3.0.0", - "listr2": "8.0.1", - "micromatch": "4.0.5", + "lilconfig": "3.1.1", + "listr2": "8.2.1", + "micromatch": "4.0.6", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.3.4" + "yaml": "2.4.2" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -3069,15 +3206,15 @@ } }, "node_modules/listr2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz", - "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", + "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.0.0", - "rfdc": "^1.3.0", + "rfdc": "^1.3.1", "wrap-ansi": "^9.0.0" }, "engines": { @@ -3390,16 +3527,28 @@ "license": "MIT" }, "node_modules/micromatch": { - "version": "4.0.5", - "license": "MIT", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz", + "integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "braces": "^3.0.3", + "picomatch": "^4.0.2" }, "engines": { "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "license": "MIT", @@ -3422,6 +3571,67 @@ "version": "2.1.2", "license": "MIT" }, + "node_modules/msw": { + "version": "2.3.0", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@inquirer/confirm": "^3.0.0", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.29.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.7.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.18.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "dev": true, @@ -3510,6 +3720,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/outvariant": { + "version": "1.4.2", + "dev": true, + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "dev": true, @@ -3602,6 +3817,11 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "6.2.2", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.0.0", "dev": true, @@ -3609,6 +3829,7 @@ }, "node_modules/picomatch": { "version": "2.3.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -3912,6 +4133,19 @@ "node": ">=10" } }, + "node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "dev": true, + "license": "MIT" + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -4033,7 +4267,8 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dependencies": { "is-number": "^7.0.0" }, @@ -4336,9 +4571,12 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -4395,6 +4633,7 @@ "husky": "^9.0.11", "jest": "^29.3.1", "lint-staged": "^15.2.2", + "msw": "^2.3.0", "prettier": "3.2.5", "ts-jest": "^29.0.5", "typedoc": "^0.23.28", diff --git a/packages/libsql-client/package.json b/packages/libsql-client/package.json index 259a9a3f..b0ec8660 100644 --- a/packages/libsql-client/package.json +++ b/packages/libsql-client/package.json @@ -114,6 +114,7 @@ "jest": "^29.3.1", "lint-staged": "^15.2.2", "prettier": "3.2.5", + "msw": "^2.3.0", "ts-jest": "^29.0.5", "typedoc": "^0.23.28", "typescript": "^4.9.4" diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index ed52c6a4..d688477c 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -9,15 +9,24 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; +import { server as mswServer } from "./mocks"; + +beforeAll(() => mswServer.listen()); +afterEach(() => mswServer.resetHandlers()); +afterAll(() => mswServer.close()); const config = { - url: process.env.URL ?? "ws://localhost:8080", - syncUrl: process.env.SYNC_URL, - authToken: process.env.AUTH_TOKEN, + url: process.env.URL ?? "ws://localhost:8080", + syncUrl: process.env.SYNC_URL, + authToken: process.env.AUTH_TOKEN, }; -const isWs = config.url.startsWith("ws:") || config.url.startsWith("wss:") || config.url.startsWith("libsql:"); -const isHttp = config.url.startsWith("http:") || config.url.startsWith("https:"); +const isWs = + config.url.startsWith("ws:") || + config.url.startsWith("wss:") || + config.url.startsWith("libsql:"); +const isHttp = + config.url.startsWith("http:") || config.url.startsWith("https:"); const isFile = config.url.startsWith("file:"); // This allows us to skip tests based on the Hrana server that we are targeting: @@ -29,897 +38,1202 @@ const server = process.env.SERVER ?? "test_v3"; const hasHrana2 = server !== "test_v1"; const hasHrana3 = server !== "test_v1" && server !== "test_v2"; -const hasNetworkErrors = isWs && (server === "test_v1" || server === "test_v2" || server === "test_v3"); +const hasNetworkErrors = + isWs && + (server === "test_v1" || server === "test_v2" || server === "test_v3"); function withClient( - f: (c: libsql.Client) => Promise, - extraConfig: Partial = {}, + f: (c: libsql.Client) => Promise, + extraConfig: Partial = {}, ): () => Promise { - return async () => { - const c = createClient({...config, ...extraConfig}); - try { - await f(c); - } finally { - c.close(); - } - }; + return async () => { + const c = createClient({ ...config, ...extraConfig }); + try { + await f(c); + } finally { + c.close(); + } + }; } function withInMemoryClient( - f: (c: libsql.Client) => Promise, + f: (c: libsql.Client) => Promise, ): () => Promise { - return async () => { - const c = createClient({ url: ":memory:" }); - try { - await f(c); - } finally { - c.close(); - } - }; + return async () => { + const c = createClient({ url: ":memory:" }); + try { + await f(c); + } finally { + c.close(); + } + }; } describe("createClient()", () => { - test("URL scheme not supported", () => { - expect(() => createClient({url: "ftp://localhost"})) - .toThrow(expect.toBeLibsqlError("URL_SCHEME_NOT_SUPPORTED", /"ftp:"/)); - }); - - test("URL param not supported", () => { - expect(() => createClient({url: "ws://localhost?foo=bar"})) - .toThrow(expect.toBeLibsqlError("URL_PARAM_NOT_SUPPORTED", /"foo"/)); - }); + test("URL scheme not supported", () => { + expect(() => createClient({ url: "ftp://localhost" })).toThrow( + expect.toBeLibsqlError("URL_SCHEME_NOT_SUPPORTED", /"ftp:"/), + ); + }); + + test("URL param not supported", () => { + expect(() => createClient({ url: "ws://localhost?foo=bar" })).toThrow( + expect.toBeLibsqlError("URL_PARAM_NOT_SUPPORTED", /"foo"/), + ); + }); + + test("URL scheme incompatible with ?tls", () => { + const urls = [ + "ws://localhost?tls=1", + "wss://localhost?tls=0", + "http://localhost?tls=1", + "https://localhost?tls=0", + ]; + for (const url of urls) { + expect(() => createClient({ url })).toThrow( + expect.toBeLibsqlError("URL_INVALID", /TLS/), + ); + } + }); - test("URL scheme incompatible with ?tls", () => { - const urls = [ - "ws://localhost?tls=1", - "wss://localhost?tls=0", - "http://localhost?tls=1", - "https://localhost?tls=0", - ]; - for (const url of urls) { - expect(() => createClient({url})) - .toThrow(expect.toBeLibsqlError("URL_INVALID", /TLS/)); - } - }); + test("missing port in libsql URL with tls=0", () => { + expect(() => createClient({ url: "libsql://localhost?tls=0" })).toThrow( + expect.toBeLibsqlError("URL_INVALID", /port/), + ); + }); - test("missing port in libsql URL with tls=0", () => { - expect(() => createClient({url: "libsql://localhost?tls=0"})) - .toThrow(expect.toBeLibsqlError("URL_INVALID", /port/)); - }); + test("invalid value of tls query param", () => { + expect(() => createClient({ url: "libsql://localhost?tls=yes" })).toThrow( + expect.toBeLibsqlError("URL_INVALID", /"tls".*"yes"/), + ); + }); - test("invalid value of tls query param", () => { - expect(() => createClient({url: "libsql://localhost?tls=yes"})) - .toThrow(expect.toBeLibsqlError("URL_INVALID", /"tls".*"yes"/)); - }); - - test("passing URL instead of config object", () => { - // @ts-expect-error - expect(() => createClient("ws://localhost")).toThrow(/as object, got string/); - }); + test("passing URL instead of config object", () => { + // @ts-expect-error + expect(() => createClient("ws://localhost")).toThrow( + /as object, got string/, + ); + }); - test("invalid value for `intMode`", () => { - // @ts-expect-error - expect(() => createClient({...config, intMode: "foo"})).toThrow(/"foo"/); - }); + test("invalid value for `intMode`", () => { + // @ts-expect-error + expect(() => createClient({ ...config, intMode: "foo" })).toThrow(/"foo"/); + }); - test("supports in-memory database", () => { - expect(() => createClient({url: ":memory:"})).not.toThrow(); - }); + test("supports in-memory database", () => { + expect(() => createClient({ url: ":memory:" })).not.toThrow(); + }); }); describe("execute()", () => { - test("query a single value", withClient(async (c) => { - const rs = await c.execute("SELECT 42"); - expect(rs.columns.length).toStrictEqual(1); - expect(rs.columnTypes.length).toStrictEqual(1); - expect(rs.rows.length).toStrictEqual(1); - expect(rs.rows[0].length).toStrictEqual(1); - expect(rs.rows[0][0]).toStrictEqual(42); - })); - - test("query a single row", withClient(async (c) => { - const rs = await c.execute("SELECT 1 AS one, 'two' AS two, 0.5 AS three"); - expect(rs.columns).toStrictEqual(["one", "two", "three"]); - expect(rs.columnTypes).toStrictEqual(["", "", ""]); - expect(rs.rows.length).toStrictEqual(1); - - const r = rs.rows[0]; - expect(r.length).toStrictEqual(3); - expect(Array.from(r)).toStrictEqual([1, "two", 0.5]); - expect(Object.entries(r)).toStrictEqual([["one", 1], ["two", "two"], ["three", 0.5]]); - })); - - test("query multiple rows", withClient(async (c) => { - const rs = await c.execute("VALUES (1, 'one'), (2, 'two'), (3, 'three')"); - expect(rs.columns.length).toStrictEqual(2); - expect(rs.columnTypes.length).toStrictEqual(2); - expect(rs.rows.length).toStrictEqual(3); - - expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); - expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); - expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); - })); - - test("statement that produces error", withClient(async (c) => { - await expect(c.execute("SELECT foobar")).rejects.toBeLibsqlError(); - })); - - test("rowsAffected with INSERT", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - ], "write"); - const rs = await c.execute("INSERT INTO t VALUES (1), (2)"); - expect(rs.rowsAffected).toStrictEqual(2); - })); - - test("rowsAffected with DELETE", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES (1), (2), (3), (4), (5)", - ], "write"); - const rs = await c.execute("DELETE FROM t WHERE a >= 3"); - expect(rs.rowsAffected).toStrictEqual(3); - })); - - test("lastInsertRowid with INSERT", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES ('one'), ('two')", - ], "write"); - const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); - expect(insertRs.lastInsertRowid).not.toBeUndefined(); - const selectRs = await c.execute({ - sql: "SELECT a FROM t WHERE ROWID = ?", - args: [insertRs.lastInsertRowid!], - }); - expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); - })); - - test("rows from INSERT RETURNING", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - ], "write"); - - const rs = await c.execute("INSERT INTO t VALUES (1) RETURNING 42 AS x, 'foo' AS y"); - expect(rs.columns).toStrictEqual(["x", "y"]); - expect(rs.columnTypes).toStrictEqual(["", ""]); - expect(rs.rows.length).toStrictEqual(1); - expect(Array.from(rs.rows[0])).toStrictEqual([42, "foo"]); - })); - - (hasHrana2 ? test : test.skip)("rowsAffected with WITH INSERT", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES (1), (2), (3)", - ], "write"); - - const rs = await c.execute(` + test( + "query a single value", + withClient(async (c) => { + const rs = await c.execute("SELECT 42"); + expect(rs.columns.length).toStrictEqual(1); + expect(rs.columnTypes.length).toStrictEqual(1); + expect(rs.rows.length).toStrictEqual(1); + expect(rs.rows[0].length).toStrictEqual(1); + expect(rs.rows[0][0]).toStrictEqual(42); + }), + ); + + test( + "query a single row", + withClient(async (c) => { + const rs = await c.execute("SELECT 1 AS one, 'two' AS two, 0.5 AS three"); + expect(rs.columns).toStrictEqual(["one", "two", "three"]); + expect(rs.columnTypes).toStrictEqual(["", "", ""]); + expect(rs.rows.length).toStrictEqual(1); + + const r = rs.rows[0]; + expect(r.length).toStrictEqual(3); + expect(Array.from(r)).toStrictEqual([1, "two", 0.5]); + expect(Object.entries(r)).toStrictEqual([ + ["one", 1], + ["two", "two"], + ["three", 0.5], + ]); + }), + ); + + test( + "query multiple rows", + withClient(async (c) => { + const rs = await c.execute("VALUES (1, 'one'), (2, 'two'), (3, 'three')"); + expect(rs.columns.length).toStrictEqual(2); + expect(rs.columnTypes.length).toStrictEqual(2); + expect(rs.rows.length).toStrictEqual(3); + + expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); + expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); + expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); + }), + ); + + test( + "statement that produces error", + withClient(async (c) => { + await expect(c.execute("SELECT foobar")).rejects.toBeLibsqlError(); + }), + ); + + test( + "rowsAffected with INSERT", + withClient(async (c) => { + await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); + const rs = await c.execute("INSERT INTO t VALUES (1), (2)"); + expect(rs.rowsAffected).toStrictEqual(2); + }), + ); + + test( + "rowsAffected with DELETE", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES (1), (2), (3), (4), (5)", + ], + "write", + ); + const rs = await c.execute("DELETE FROM t WHERE a >= 3"); + expect(rs.rowsAffected).toStrictEqual(3); + }), + ); + + test( + "lastInsertRowid with INSERT", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES ('one'), ('two')", + ], + "write", + ); + const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); + expect(insertRs.lastInsertRowid).not.toBeUndefined(); + const selectRs = await c.execute({ + sql: "SELECT a FROM t WHERE ROWID = ?", + args: [insertRs.lastInsertRowid!], + }); + expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); + }), + ); + + test( + "rows from INSERT RETURNING", + withClient(async (c) => { + await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); + + const rs = await c.execute( + "INSERT INTO t VALUES (1) RETURNING 42 AS x, 'foo' AS y", + ); + expect(rs.columns).toStrictEqual(["x", "y"]); + expect(rs.columnTypes).toStrictEqual(["", ""]); + expect(rs.rows.length).toStrictEqual(1); + expect(Array.from(rs.rows[0])).toStrictEqual([42, "foo"]); + }), + ); + + (hasHrana2 ? test : test.skip)( + "rowsAffected with WITH INSERT", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES (1), (2), (3)", + ], + "write", + ); + + const rs = await c.execute(` WITH x(a) AS (SELECT 2*a FROM t) INSERT INTO t SELECT a+1 FROM x `); - expect(rs.rowsAffected).toStrictEqual(3); - })); - - test("query a single value using an in memory database", withInMemoryClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES ('one'), ('two')", - ], "write"); - const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); - expect(insertRs.lastInsertRowid).not.toBeUndefined(); - const selectRs = await c.execute({ - sql: "SELECT a FROM t WHERE ROWID = ?", - args: [insertRs.lastInsertRowid!], - }); - expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); - })); + expect(rs.rowsAffected).toStrictEqual(3); + }), + ); + + test( + "query a single value using an in memory database", + withInMemoryClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES ('one'), ('two')", + ], + "write", + ); + const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); + expect(insertRs.lastInsertRowid).not.toBeUndefined(); + const selectRs = await c.execute({ + sql: "SELECT a FROM t WHERE ROWID = ?", + args: [insertRs.lastInsertRowid!], + }); + expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); + }), + ); }); describe("values", () => { - function testRoundtrip( - name: string, - passed: libsql.InValue, - expected: libsql.Value, - intMode?: libsql.IntMode, - ): void { - test(name, withClient(async (c) => { - const rs = await c.execute({sql: "SELECT ?", args: [passed]}); - expect(rs.rows[0][0]).toStrictEqual(expected); - }, {intMode})); - } - - function testRoundtripError( - name: string, - passed: libsql.InValue, - expectedError: unknown, - intMode?: libsql.IntMode, - ): void { - test(name, withClient(async (c) => { - await expect(c.execute({ - sql: "SELECT ?", - args: [passed], - })).rejects.toBeInstanceOf(expectedError); - }, {intMode})); + function testRoundtrip( + name: string, + passed: libsql.InValue, + expected: libsql.Value, + intMode?: libsql.IntMode, + ): void { + test( + name, + withClient( + async (c) => { + const rs = await c.execute({ sql: "SELECT ?", args: [passed] }); + expect(rs.rows[0][0]).toStrictEqual(expected); + }, + { intMode }, + ), + ); + } + + function testRoundtripError( + name: string, + passed: libsql.InValue, + expectedError: unknown, + intMode?: libsql.IntMode, + ): void { + test( + name, + withClient( + async (c) => { + await expect( + c.execute({ + sql: "SELECT ?", + args: [passed], + }), + ).rejects.toBeInstanceOf(expectedError); + }, + { intMode }, + ), + ); + } + + testRoundtrip("string", "boomerang", "boomerang"); + testRoundtrip("string with weird characters", "a\n\r\t ", "a\n\r\t "); + testRoundtrip( + "string with unicode", + "žluťoučký kůň úpěl ďábelské ódy", + "žluťoučký kůň úpěl ďábelské ódy", + ); + + describe("number", () => { + const intModes: Array = ["number", "bigint", "string"]; + for (const intMode of intModes) { + testRoundtrip("zero", 0, 0, intMode); + testRoundtrip("integer", -2023, -2023, intMode); + testRoundtrip("float", 12.345, 12.345, intMode); + testRoundtrip("large positive float", 1e18, 1e18, intMode); + testRoundtrip("large negative float", -1e18, -1e18, intMode); + testRoundtrip("MAX_VALUE", Number.MAX_VALUE, Number.MAX_VALUE, intMode); + testRoundtrip( + "-MAX_VALUE", + -Number.MAX_VALUE, + -Number.MAX_VALUE, + intMode, + ); + testRoundtrip("MIN_VALUE", Number.MIN_VALUE, Number.MIN_VALUE, intMode); } - - testRoundtrip("string", "boomerang", "boomerang"); - testRoundtrip("string with weird characters", "a\n\r\t ", "a\n\r\t "); - testRoundtrip("string with unicode", - "žluťoučký kůň úpěl ďábelské ódy", "žluťoučký kůň úpěl ďábelské ódy"); - - describe("number", () => { - const intModes: Array = ["number", "bigint", "string"]; - for (const intMode of intModes) { - testRoundtrip("zero", 0, 0, intMode); - testRoundtrip("integer", -2023, -2023, intMode); - testRoundtrip("float", 12.345, 12.345, intMode); - testRoundtrip("large positive float", 1e18, 1e18, intMode); - testRoundtrip("large negative float", -1e18, -1e18, intMode); - testRoundtrip("MAX_VALUE", Number.MAX_VALUE, Number.MAX_VALUE, intMode); - testRoundtrip("-MAX_VALUE", -Number.MAX_VALUE, -Number.MAX_VALUE, intMode); - testRoundtrip("MIN_VALUE", Number.MIN_VALUE, Number.MIN_VALUE, intMode); - } + }); + + describe("bigint", () => { + describe("'number' int mode", () => { + testRoundtrip("zero integer", 0n, 0, "number"); + testRoundtrip("small integer", -42n, -42, "number"); + testRoundtrip( + "largest safe integer", + 9007199254740991n, + 9007199254740991, + "number", + ); + testRoundtripError( + "smallest unsafe integer", + 9007199254740992n, + RangeError, + "number", + ); + testRoundtripError( + "large unsafe integer", + -1152921504594532842n, + RangeError, + "number", + ); }); - describe("bigint", () => { - describe("'number' int mode", () => { - testRoundtrip("zero integer", 0n, 0, "number"); - testRoundtrip("small integer", -42n, -42, "number"); - testRoundtrip("largest safe integer", 9007199254740991n, 9007199254740991, "number"); - testRoundtripError("smallest unsafe integer", 9007199254740992n, RangeError, "number"); - testRoundtripError("large unsafe integer", -1152921504594532842n, RangeError, "number"); - }); - - describe("'bigint' int mode", () => { - testRoundtrip("zero integer", 0n, 0n, "bigint"); - testRoundtrip("small integer", -42n, -42n, "bigint"); - testRoundtrip("large positive integer", 1152921504608088318n, 1152921504608088318n, "bigint"); - testRoundtrip("large negative integer", -1152921504594532842n, -1152921504594532842n, "bigint"); - testRoundtrip("largest positive integer", 9223372036854775807n, 9223372036854775807n, "bigint"); - testRoundtrip("largest negative integer", -9223372036854775808n, -9223372036854775808n, "bigint"); - }); - - describe("'string' int mode", () => { - testRoundtrip("zero integer", 0n, "0", "string"); - testRoundtrip("small integer", -42n, "-42", "string"); - testRoundtrip("large positive integer", 1152921504608088318n, "1152921504608088318", "string"); - testRoundtrip("large negative integer", -1152921504594532842n, "-1152921504594532842", "string"); - testRoundtrip("largest positive integer", 9223372036854775807n, "9223372036854775807", "string"); - testRoundtrip("largest negative integer", -9223372036854775808n, "-9223372036854775808", "string"); - }); + describe("'bigint' int mode", () => { + testRoundtrip("zero integer", 0n, 0n, "bigint"); + testRoundtrip("small integer", -42n, -42n, "bigint"); + testRoundtrip( + "large positive integer", + 1152921504608088318n, + 1152921504608088318n, + "bigint", + ); + testRoundtrip( + "large negative integer", + -1152921504594532842n, + -1152921504594532842n, + "bigint", + ); + testRoundtrip( + "largest positive integer", + 9223372036854775807n, + 9223372036854775807n, + "bigint", + ); + testRoundtrip( + "largest negative integer", + -9223372036854775808n, + -9223372036854775808n, + "bigint", + ); }); - const buf = new ArrayBuffer(256); - const array = new Uint8Array(buf); - for (let i = 0; i < 256; ++i) { - array[i] = i ^ 0xab; - } - testRoundtrip("ArrayBuffer", buf, buf); - testRoundtrip("Uint8Array", array, buf); - - testRoundtrip("null", null, null); - testRoundtrip("true", true, 1n, "bigint"); - testRoundtrip("false", false, 0n, "bigint"); - testRoundtrip("true", true, 1, "number"); - testRoundtrip("false", false, 0, "number"); - testRoundtrip("true", true, "1", "string"); - testRoundtrip("false", false, "0", "string"); - testRoundtrip("true", true, 1); - testRoundtrip("false", false, 0); - - testRoundtrip("Date", new Date("2023-01-02T12:34:56Z"), 1672662896000, "bigint"); - - // @ts-expect-error - testRoundtripError("undefined produces error", undefined, TypeError); - testRoundtripError("NaN produces error", NaN, RangeError); - testRoundtripError("Infinity produces error", Infinity, RangeError); - testRoundtripError("large bigint produces error", -1267650600228229401496703205376n, RangeError); - - test("max 64-bit bigint", withClient(async (c) => { - const rs = await c.execute({sql: "SELECT ?||''", args: [9223372036854775807n]}); - expect(rs.rows[0][0]).toStrictEqual("9223372036854775807"); - })); - - test("min 64-bit bigint", withClient(async (c) => { - const rs = await c.execute({sql: "SELECT ?||''", args: [-9223372036854775808n]}); - expect(rs.rows[0][0]).toStrictEqual("-9223372036854775808"); - })); + describe("'string' int mode", () => { + testRoundtrip("zero integer", 0n, "0", "string"); + testRoundtrip("small integer", -42n, "-42", "string"); + testRoundtrip( + "large positive integer", + 1152921504608088318n, + "1152921504608088318", + "string", + ); + testRoundtrip( + "large negative integer", + -1152921504594532842n, + "-1152921504594532842", + "string", + ); + testRoundtrip( + "largest positive integer", + 9223372036854775807n, + "9223372036854775807", + "string", + ); + testRoundtrip( + "largest negative integer", + -9223372036854775808n, + "-9223372036854775808", + "string", + ); + }); + }); + + const buf = new ArrayBuffer(256); + const array = new Uint8Array(buf); + for (let i = 0; i < 256; ++i) { + array[i] = i ^ 0xab; + } + testRoundtrip("ArrayBuffer", buf, buf); + testRoundtrip("Uint8Array", array, buf); + + testRoundtrip("null", null, null); + testRoundtrip("true", true, 1n, "bigint"); + testRoundtrip("false", false, 0n, "bigint"); + testRoundtrip("true", true, 1, "number"); + testRoundtrip("false", false, 0, "number"); + testRoundtrip("true", true, "1", "string"); + testRoundtrip("false", false, "0", "string"); + testRoundtrip("true", true, 1); + testRoundtrip("false", false, 0); + + testRoundtrip( + "Date", + new Date("2023-01-02T12:34:56Z"), + 1672662896000, + "bigint", + ); + + // @ts-expect-error + testRoundtripError("undefined produces error", undefined, TypeError); + testRoundtripError("NaN produces error", NaN, RangeError); + testRoundtripError("Infinity produces error", Infinity, RangeError); + testRoundtripError( + "large bigint produces error", + -1267650600228229401496703205376n, + RangeError, + ); + + test( + "max 64-bit bigint", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?||''", + args: [9223372036854775807n], + }); + expect(rs.rows[0][0]).toStrictEqual("9223372036854775807"); + }), + ); + + test( + "min 64-bit bigint", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?||''", + args: [-9223372036854775808n], + }); + expect(rs.rows[0][0]).toStrictEqual("-9223372036854775808"); + }), + ); }); describe("ResultSet.toJSON()", () => { - test("simple result set", withClient(async (c) => { - const rs = await c.execute("SELECT 1 AS a"); - const json = rs.toJSON(); - expect(json["lastInsertRowid"] === null || json["lastInsertRowid"] === "0").toBe(true); - expect(json["columns"]).toStrictEqual(["a"]); - expect(json["columnTypes"]).toStrictEqual([""]); - expect(json["rows"]).toStrictEqual([[1]]); - expect(json["rowsAffected"]).toStrictEqual(0); - - const str = JSON.stringify(rs); - expect( - str === '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":null}' || - str === '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":"0"}' - ).toBe(true); - })); - - test("lastInsertRowid", withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (id INTEGER PRIMARY KEY NOT NULL)"); - const rs = await c.execute("INSERT INTO t VALUES (12345)"); - expect(rs.toJSON()).toStrictEqual({ - "columns": [], - "columnTypes": [], - "rows": [], - "rowsAffected": 1, - "lastInsertRowid": "12345", - }); - })); - - test("computed values", withClient(async (c) => { - const rs = await c.execute( - "SELECT 42 AS integer, 0.5 AS float, NULL AS \"null\", 'foo' AS text, X'626172' AS blob", - ); - const json = rs.toJSON(); - expect(json["columns"]).toStrictEqual(["integer", "float", "null", "text", "blob"]); - expect(json["columnTypes"]).toStrictEqual(["", "", "", "", ""]); - expect(json["rows"]).toStrictEqual([[42, 0.5, null, "foo", "YmFy"]]); - })); - - (hasHrana2 ? test : test.skip)("row values", withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (i INTEGER, f FLOAT, t TEXT, b BLOB)"); - await c.execute("INSERT INTO t VALUES (42, 0.5, 'foo', X'626172')"); - const rs = await c.execute( - "SELECT i, f, t, b FROM t LIMIT 1", - ); - const json = rs.toJSON(); - expect(json["columns"]).toStrictEqual(["i", "f", "t", "b"]); - expect(json["columnTypes"]).toStrictEqual(["INTEGER", "FLOAT", "TEXT", "BLOB"]); - expect(json["rows"]).toStrictEqual([[42, 0.5, "foo", "YmFy"]]); - })); - - test("bigint row value", withClient(async (c) => { + test( + "simple result set", + withClient(async (c) => { + const rs = await c.execute("SELECT 1 AS a"); + const json = rs.toJSON(); + expect( + json["lastInsertRowid"] === null || json["lastInsertRowid"] === "0", + ).toBe(true); + expect(json["columns"]).toStrictEqual(["a"]); + expect(json["columnTypes"]).toStrictEqual([""]); + expect(json["rows"]).toStrictEqual([[1]]); + expect(json["rowsAffected"]).toStrictEqual(0); + + const str = JSON.stringify(rs); + expect( + str === + '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":null}' || + str === + '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":"0"}', + ).toBe(true); + }), + ); + + test( + "lastInsertRowid", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (id INTEGER PRIMARY KEY NOT NULL)"); + const rs = await c.execute("INSERT INTO t VALUES (12345)"); + expect(rs.toJSON()).toStrictEqual({ + columns: [], + columnTypes: [], + rows: [], + rowsAffected: 1, + lastInsertRowid: "12345", + }); + }), + ); + + test( + "computed values", + withClient(async (c) => { + const rs = await c.execute( + "SELECT 42 AS integer, 0.5 AS float, NULL AS \"null\", 'foo' AS text, X'626172' AS blob", + ); + const json = rs.toJSON(); + expect(json["columns"]).toStrictEqual([ + "integer", + "float", + "null", + "text", + "blob", + ]); + expect(json["columnTypes"]).toStrictEqual(["", "", "", "", ""]); + expect(json["rows"]).toStrictEqual([[42, 0.5, null, "foo", "YmFy"]]); + }), + ); + + (hasHrana2 ? test : test.skip)( + "row values", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (i INTEGER, f FLOAT, t TEXT, b BLOB)"); + await c.execute("INSERT INTO t VALUES (42, 0.5, 'foo', X'626172')"); + const rs = await c.execute("SELECT i, f, t, b FROM t LIMIT 1"); + const json = rs.toJSON(); + expect(json["columns"]).toStrictEqual(["i", "f", "t", "b"]); + expect(json["columnTypes"]).toStrictEqual([ + "INTEGER", + "FLOAT", + "TEXT", + "BLOB", + ]); + expect(json["rows"]).toStrictEqual([[42, 0.5, "foo", "YmFy"]]); + }), + ); + + test( + "bigint row value", + withClient( + async (c) => { const rs = await c.execute("SELECT 42"); const json = rs.toJSON(); expect(json["rows"]).toStrictEqual([["42"]]); - }, {intMode: "bigint"})); + }, + { intMode: "bigint" }, + ), + ); }); describe("arguments", () => { - test("? arguments", withClient(async (c) => { - const rs = await c.execute({ - sql: "SELECT ?, ?", - args: ["one", "two"], - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["one", "two"]); - })); - - (!isFile ? test : test.skip)("?NNN arguments", withClient(async (c) => { + test( + "? arguments", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?, ?", + args: ["one", "two"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual(["one", "two"]); + }), + ); + + (!isFile ? test : test.skip)( + "?NNN arguments", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?2, ?3, ?1", + args: ["one", "two", "three"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual(["two", "three", "one"]); + }), + ); + + (!isFile ? test : test.skip)( + "?NNN arguments with holes", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?3, ?1", + args: ["one", "two", "three"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual(["three", "one"]); + }), + ); + + (!isFile ? test : test.skip)( + "?NNN and ? arguments", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?2, ?, ?3", + args: ["one", "two", "three"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual(["two", "three", "three"]); + }), + ); + + for (const sign of [":", "@", "$"]) { + test( + `${sign}AAAA arguments`, + withClient(async (c) => { const rs = await c.execute({ - sql: "SELECT ?2, ?3, ?1", - args: ["one", "two", "three"], + sql: `SELECT ${sign}b, ${sign}a`, + args: { a: "one", [`${sign}b`]: "two" }, }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "three", "one"]); - })); + expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one"]); + }), + ); - (!isFile ? test : test.skip)("?NNN arguments with holes", withClient(async (c) => { + test( + `${sign}AAAA arguments used multiple times`, + withClient(async (c) => { const rs = await c.execute({ - sql: "SELECT ?3, ?1", - args: ["one", "two", "three"], + sql: `SELECT ${sign}b, ${sign}a, ${sign}b || ${sign}a`, + args: { a: "one", [`${sign}b`]: "two" }, }); - expect(Array.from(rs.rows[0])).toStrictEqual(["three", "one"]); - })); + expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one", "twoone"]); + }), + ); - (!isFile ? test : test.skip)("?NNN and ? arguments", withClient(async (c) => { + test( + `${sign}AAAA arguments and ?NNN arguments`, + withClient(async (c) => { const rs = await c.execute({ - sql: "SELECT ?2, ?, ?3", - args: ["one", "two", "three"], + sql: `SELECT ${sign}b, ${sign}a, ?1`, + args: { a: "one", [`${sign}b`]: "two" }, }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "three", "three"]); - })); - - for (const sign of [":", "@", "$"]) { - test(`${sign}AAAA arguments`, withClient(async (c) => { - const rs = await c.execute({ - sql: `SELECT ${sign}b, ${sign}a`, - args: {"a": "one", [`${sign}b`]: "two"}, - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one"]); - })); - - test(`${sign}AAAA arguments used multiple times`, withClient(async (c) => { - const rs = await c.execute({ - sql: `SELECT ${sign}b, ${sign}a, ${sign}b || ${sign}a`, - args: {"a": "one", [`${sign}b`]: "two"}, - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one", "twoone"]); - })); - - test(`${sign}AAAA arguments and ?NNN arguments`, withClient(async (c) => { - const rs = await c.execute({ - sql: `SELECT ${sign}b, ${sign}a, ?1`, - args: {"a": "one", [`${sign}b`]: "two"}, - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one", "two"]); - })); - } + expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one", "two"]); + }), + ); + } }); describe("batch()", () => { - test("multiple queries", withClient(async (c) => { - const rss = await c.batch([ - "SELECT 1+1", - "SELECT 1 AS one, 2 AS two", - {sql: "SELECT ?", args: ["boomerang"]}, - {sql: "VALUES (?), (?)", args: ["big", "ben"]}, - ], "read"); - - expect(rss.length).toStrictEqual(4); - const [rs0, rs1, rs2, rs3] = rss; - - expect(rs0.rows.length).toStrictEqual(1); - expect(Array.from(rs0.rows[0])).toStrictEqual([2]); - - expect(rs1.rows.length).toStrictEqual(1); - expect(Array.from(rs1.rows[0])).toStrictEqual([1, 2]); - - expect(rs2.rows.length).toStrictEqual(1); - expect(Array.from(rs2.rows[0])).toStrictEqual(["boomerang"]); - - expect(rs3.rows.length).toStrictEqual(2); - expect(Array.from(rs3.rows[0])).toStrictEqual(["big"]); - expect(Array.from(rs3.rows[1])).toStrictEqual(["ben"]); - })); - - test("statements are executed sequentially", withClient(async (c) => { - const rss = await c.batch([ - /* 0 */ "DROP TABLE IF EXISTS t", - /* 1 */ "CREATE TABLE t (a, b)", - /* 2 */ "INSERT INTO t VALUES (1, 'one')", - /* 3 */ "SELECT * FROM t ORDER BY a", - /* 4 */ "INSERT INTO t VALUES (2, 'two')", - /* 5 */ "SELECT * FROM t ORDER BY a", - /* 6 */ "DROP TABLE t", - ], "write"); - - expect(rss.length).toStrictEqual(7); - expect(rss[3].rows).toEqual([ - {a: 1, b: "one"}, - ]); - expect(rss[5].rows).toEqual([ - {a: 1, b: "one"}, - {a: 2, b: "two"}, - ]); - })); - - test("statements are executed in a transaction", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t1", - "DROP TABLE IF EXISTS t2", - "CREATE TABLE t1 (a)", - "CREATE TABLE t2 (a)", - ], "write"); - - const n = 100; - const promises = []; - for (let i = 0; i < n; ++i) { - const ii = i; - promises.push((async () => { - const rss = await c.batch([ - {sql: "INSERT INTO t1 VALUES (?)", args: [ii]}, - {sql: "INSERT INTO t2 VALUES (?)", args: [ii * 10]}, - "SELECT SUM(a) FROM t1", - "SELECT SUM(a) FROM t2", - ], "write"); - - const sum1 = rss[2].rows[0][0] as number; - const sum2 = rss[3].rows[0][0] as number; - expect(sum2).toStrictEqual(sum1 * 10); - })()); - } - await Promise.all(promises); - - const rs1 = await c.execute("SELECT SUM(a) FROM t1"); - expect(rs1.rows[0][0]).toStrictEqual(n*(n-1)/2); - const rs2 = await c.execute("SELECT SUM(a) FROM t2"); - expect(rs2.rows[0][0]).toStrictEqual(n*(n-1)/2*10); - }), 10000); - - test("error in batch", withClient(async (c) => { - await expect(c.batch([ - "SELECT 1+1", - "SELECT foobar", - ], "read")).rejects.toBeLibsqlError(); - })); - - test("error in batch rolls back transaction", withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a)"); - await c.execute("INSERT INTO t VALUES ('one')"); - await expect(c.batch([ + test( + "multiple queries", + withClient(async (c) => { + const rss = await c.batch( + [ + "SELECT 1+1", + "SELECT 1 AS one, 2 AS two", + { sql: "SELECT ?", args: ["boomerang"] }, + { sql: "VALUES (?), (?)", args: ["big", "ben"] }, + ], + "read", + ); + + expect(rss.length).toStrictEqual(4); + const [rs0, rs1, rs2, rs3] = rss; + + expect(rs0.rows.length).toStrictEqual(1); + expect(Array.from(rs0.rows[0])).toStrictEqual([2]); + + expect(rs1.rows.length).toStrictEqual(1); + expect(Array.from(rs1.rows[0])).toStrictEqual([1, 2]); + + expect(rs2.rows.length).toStrictEqual(1); + expect(Array.from(rs2.rows[0])).toStrictEqual(["boomerang"]); + + expect(rs3.rows.length).toStrictEqual(2); + expect(Array.from(rs3.rows[0])).toStrictEqual(["big"]); + expect(Array.from(rs3.rows[1])).toStrictEqual(["ben"]); + }), + ); + + test( + "statements are executed sequentially", + withClient(async (c) => { + const rss = await c.batch( + [ + /* 0 */ "DROP TABLE IF EXISTS t", + /* 1 */ "CREATE TABLE t (a, b)", + /* 2 */ "INSERT INTO t VALUES (1, 'one')", + /* 3 */ "SELECT * FROM t ORDER BY a", + /* 4 */ "INSERT INTO t VALUES (2, 'two')", + /* 5 */ "SELECT * FROM t ORDER BY a", + /* 6 */ "DROP TABLE t", + ], + "write", + ); + + expect(rss.length).toStrictEqual(7); + expect(rss[3].rows).toEqual([{ a: 1, b: "one" }]); + expect(rss[5].rows).toEqual([ + { a: 1, b: "one" }, + { a: 2, b: "two" }, + ]); + }), + ); + + test( + "statements are executed in a transaction", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t1", + "DROP TABLE IF EXISTS t2", + "CREATE TABLE t1 (a)", + "CREATE TABLE t2 (a)", + ], + "write", + ); + + const n = 100; + const promises = []; + for (let i = 0; i < n; ++i) { + const ii = i; + promises.push( + (async () => { + const rss = await c.batch( + [ + { sql: "INSERT INTO t1 VALUES (?)", args: [ii] }, + { sql: "INSERT INTO t2 VALUES (?)", args: [ii * 10] }, + "SELECT SUM(a) FROM t1", + "SELECT SUM(a) FROM t2", + ], + "write", + ); + + const sum1 = rss[2].rows[0][0] as number; + const sum2 = rss[3].rows[0][0] as number; + expect(sum2).toStrictEqual(sum1 * 10); + })(), + ); + } + await Promise.all(promises); + + const rs1 = await c.execute("SELECT SUM(a) FROM t1"); + expect(rs1.rows[0][0]).toStrictEqual((n * (n - 1)) / 2); + const rs2 = await c.execute("SELECT SUM(a) FROM t2"); + expect(rs2.rows[0][0]).toStrictEqual(((n * (n - 1)) / 2) * 10); + }), + 10000, + ); + + test( + "error in batch", + withClient(async (c) => { + await expect( + c.batch(["SELECT 1+1", "SELECT foobar"], "read"), + ).rejects.toBeLibsqlError(); + }), + ); + + test( + "error in batch rolls back transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a)"); + await c.execute("INSERT INTO t VALUES ('one')"); + await expect( + c.batch( + [ "INSERT INTO t VALUES ('two')", "SELECT foobar", "INSERT INTO t VALUES ('three')", - ], "write")).rejects.toBeLibsqlError(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(1); - })); - - test("batch with a lot of different statements", withClient(async (c) => { - const stmts = []; - for (let i = 0; i < 1000; ++i) { - stmts.push(`SELECT ${i}`); + ], + "write", + ), + ).rejects.toBeLibsqlError(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(1); + }), + ); + + test( + "batch with a lot of different statements", + withClient(async (c) => { + const stmts = []; + for (let i = 0; i < 1000; ++i) { + stmts.push(`SELECT ${i}`); + } + const rss = await c.batch(stmts, "read"); + for (let i = 0; i < stmts.length; ++i) { + expect(rss[i].rows[0][0]).toStrictEqual(i); + } + }), + ); + + test( + "batch with a lot of the same statements", + withClient(async (c) => { + const n = 20; + const m = 200; + + const stmts = []; + for (let i = 0; i < n; ++i) { + for (let j = 0; j < m; ++j) { + stmts.push({ sql: `SELECT ?, ${j}`, args: [i] }); } - const rss = await c.batch(stmts, "read"); - for (let i = 0; i < stmts.length; ++i) { - expect(rss[i].rows[0][0]).toStrictEqual(i); - } - })); - - test("batch with a lot of the same statements", withClient(async (c) => { - const n = 20; - const m = 200; - - const stmts = []; - for (let i = 0; i < n; ++i) { - for (let j = 0; j < m; ++j) { - stmts.push({sql: `SELECT ?, ${j}`, args: [i]}); - } - } - - const rss = await c.batch(stmts, "read"); - for (let i = 0; i < n; ++i) { - for (let j = 0; j < m; ++j) { - const rs = rss[i*m + j]; - expect(rs.rows[0][0]).toStrictEqual(i); - expect(rs.rows[0][1]).toStrictEqual(j); - } + } + + const rss = await c.batch(stmts, "read"); + for (let i = 0; i < n; ++i) { + for (let j = 0; j < m; ++j) { + const rs = rss[i * m + j]; + expect(rs.rows[0][0]).toStrictEqual(i); + expect(rs.rows[0][1]).toStrictEqual(j); } - })); - - test("deferred batch", withClient(async (c) => { - const rss = await c.batch([ - "SELECT 1+1", - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES (21) RETURNING 2*a", - ], "deferred"); - - expect(rss.length).toStrictEqual(4); - const [rs0, _rs1, _rs2, rs3] = rss; - - expect(rs0.rows.length).toStrictEqual(1); - expect(Array.from(rs0.rows[0])).toStrictEqual([2]); - - expect(rs3.rows.length).toStrictEqual(1); - expect(Array.from(rs3.rows[0])).toStrictEqual([42]); - })); - - (hasHrana3 ? test : test.skip)("ROLLBACK statement stops execution of batch", withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a)"); - - await expect(c.batch([ + } + }), + ); + + test( + "deferred batch", + withClient(async (c) => { + const rss = await c.batch( + [ + "SELECT 1+1", + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES (21) RETURNING 2*a", + ], + "deferred", + ); + + expect(rss.length).toStrictEqual(4); + const [rs0, _rs1, _rs2, rs3] = rss; + + expect(rs0.rows.length).toStrictEqual(1); + expect(Array.from(rs0.rows[0])).toStrictEqual([2]); + + expect(rs3.rows.length).toStrictEqual(1); + expect(Array.from(rs3.rows[0])).toStrictEqual([42]); + }), + ); + + (hasHrana3 ? test : test.skip)( + "ROLLBACK statement stops execution of batch", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a)"); + + await expect( + c.batch( + [ "INSERT INTO t VALUES (1), (2), (3)", "ROLLBACK", "INSERT INTO t VALUES (4), (5)", - ], "write")).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - })); - - test.only("with wait set to true", withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a)"); + ], + "write", + ), + ).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); + + test.only( + "with wait set to true", + withClient(async (c) => { + //await c.execute("DROP TABLE IF EXISTS t"); + //await c.execute("CREATE TABLE t (a)"); const startTs = Date.now(); - await c.batch([ - "ALTER TABLE t ADD COLUMN phone_number", - "ALTER TABLE t ADD COLUMN another_column" - ], { - transactionMode:"read", - wait: true - }); + await c.batch( + ["CREATE TABLE t (a)", "ALTER TABLE t ADD COLUMN another_column"], + { + transactionMode: "read", + wait: true, + }, + ); const durationInMs = Date.now() - startTs; expect(durationInMs).toBeGreaterThanOrEqual(1000); - })); + }), + ); }); describe("transaction()", () => { - test("query multiple rows", withClient(async (c) => { - const txn = await c.transaction("read"); - - const rs = await txn.execute("VALUES (1, 'one'), (2, 'two'), (3, 'three')"); - expect(rs.columns.length).toStrictEqual(2); - expect(rs.columnTypes.length).toStrictEqual(2); - expect(rs.rows.length).toStrictEqual(3); - - expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); - expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); - expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); - - txn.close(); - })); - - test("commit()", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - ], "write"); - + test( + "query multiple rows", + withClient(async (c) => { + const txn = await c.transaction("read"); + + const rs = await txn.execute( + "VALUES (1, 'one'), (2, 'two'), (3, 'three')", + ); + expect(rs.columns.length).toStrictEqual(2); + expect(rs.columnTypes.length).toStrictEqual(2); + expect(rs.rows.length).toStrictEqual(3); + + expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); + expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); + expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); + + txn.close(); + }), + ); + + test( + "commit()", + withClient(async (c) => { + await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); + + const txn = await c.transaction("write"); + await txn.execute("INSERT INTO t VALUES ('one')"); + await txn.execute("INSERT INTO t VALUES ('two')"); + expect(txn.closed).toStrictEqual(false); + await txn.commit(); + expect(txn.closed).toStrictEqual(true); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(2); + await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + }), + ); + + test( + "rollback()", + withClient(async (c) => { + await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); + + const txn = await c.transaction("write"); + await txn.execute("INSERT INTO t VALUES ('one')"); + await txn.execute("INSERT INTO t VALUES ('two')"); + expect(txn.closed).toStrictEqual(false); + await txn.rollback(); + expect(txn.closed).toStrictEqual(true); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + }), + ); + + test( + "close()", + withClient(async (c) => { + await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); + + const txn = await c.transaction("write"); + await txn.execute("INSERT INTO t VALUES ('one')"); + expect(txn.closed).toStrictEqual(false); + txn.close(); + expect(txn.closed).toStrictEqual(true); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + }), + ); + + test( + "error does not rollback", + withClient(async (c) => { + await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); + + const txn = await c.transaction("write"); + await expect(txn.execute("SELECT foo")).rejects.toBeLibsqlError(); + await txn.execute("INSERT INTO t VALUES ('one')"); + await expect(txn.execute("SELECT bar")).rejects.toBeLibsqlError(); + await txn.commit(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(1); + }), + ); + + (hasHrana3 ? test : test.skip)( + "ROLLBACK statement stops execution of transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a)"); + + const txn = await c.transaction("write"); + const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); + const prom2 = txn.execute("ROLLBACK"); + const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); + + await prom1; + await prom2; + await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + await expect(txn.commit()).rejects.toBeLibsqlError(); + txn.close(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); + + (hasHrana3 ? test : test.skip)( + "OR ROLLBACK statement stops execution of transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a UNIQUE)"); + + const txn = await c.transaction("write"); + const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); + const prom2 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); + const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); + + await prom1; + await expect(prom2).rejects.toBeLibsqlError(); + await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + await expect(txn.commit()).rejects.toBeLibsqlError(); + txn.close(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); + + (hasHrana3 ? test : test.skip)( + "OR ROLLBACK as the first statement stops execution of transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a UNIQUE)"); + await c.execute("INSERT INTO t VALUES (1), (2), (3)"); + + const txn = await c.transaction("write"); + const prom1 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); + const prom2 = txn.execute("INSERT INTO t VALUES (4), (5)"); + + await expect(prom1).rejects.toBeLibsqlError(); + await expect(prom2).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + await expect(txn.commit()).rejects.toBeLibsqlError(); + txn.close(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(3); + }), + ); + + test( + "commit empty", + withClient(async (c) => { + const txn = await c.transaction("read"); + await txn.commit(); + }), + ); + + test( + "rollback empty", + withClient(async (c) => { + const txn = await c.transaction("read"); + await txn.rollback(); + }), + ); + + describe("batch()", () => { + test( + "as the first operation on transaction", + withClient(async (c) => { const txn = await c.transaction("write"); - await txn.execute("INSERT INTO t VALUES ('one')"); - await txn.execute("INSERT INTO t VALUES ('two')"); - expect(txn.closed).toStrictEqual(false); - await txn.commit(); - expect(txn.closed).toStrictEqual(true); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(2); - await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - })); - - test("rollback()", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - ], "write"); - const txn = await c.transaction("write"); - await txn.execute("INSERT INTO t VALUES ('one')"); - await txn.execute("INSERT INTO t VALUES ('two')"); - expect(txn.closed).toStrictEqual(false); - await txn.rollback(); - expect(txn.closed).toStrictEqual(true); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - })); - - test("close()", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - ], "write"); + await txn.batch([ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + { sql: "INSERT INTO t VALUES (?)", args: [1] }, + { sql: "INSERT INTO t VALUES (?)", args: [2] }, + { sql: "INSERT INTO t VALUES (?)", args: [4] }, + ]); - const txn = await c.transaction("write"); - await txn.execute("INSERT INTO t VALUES ('one')"); - expect(txn.closed).toStrictEqual(false); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); txn.close(); - expect(txn.closed).toStrictEqual(true); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - })); - - test("error does not rollback", withClient(async (c) => { - await c.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - ], "write"); + }), + ); + test( + "as the second operation on transaction", + withClient(async (c) => { const txn = await c.transaction("write"); - await expect(txn.execute("SELECT foo")).rejects.toBeLibsqlError(); - await txn.execute("INSERT INTO t VALUES ('one')"); - await expect(txn.execute("SELECT bar")).rejects.toBeLibsqlError(); - await txn.commit(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(1); - })); - - (hasHrana3 ? test : test.skip)( - "ROLLBACK statement stops execution of transaction", - withClient(async (c) => - { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a)"); - const txn = await c.transaction("write"); - const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); - const prom2 = txn.execute("ROLLBACK"); - const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); - - await prom1; - await prom2; - await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - await expect(txn.commit()).rejects.toBeLibsqlError(); - txn.close(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - })); - - (hasHrana3 ? test : test.skip)( - "OR ROLLBACK statement stops execution of transaction", - withClient(async (c) => - { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a UNIQUE)"); + await txn.execute("DROP TABLE IF EXISTS t"); + await txn.batch([ + "CREATE TABLE t (a)", + { sql: "INSERT INTO t VALUES (?)", args: [1] }, + { sql: "INSERT INTO t VALUES (?)", args: [2] }, + { sql: "INSERT INTO t VALUES (?)", args: [4] }, + ]); - const txn = await c.transaction("write"); - const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); - const prom2 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); - const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); - - await prom1; - await expect(prom2).rejects.toBeLibsqlError(); - await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - await expect(txn.commit()).rejects.toBeLibsqlError(); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); txn.close(); + }), + ); - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - })); - - (hasHrana3 ? test : test.skip)( - "OR ROLLBACK as the first statement stops execution of transaction", - withClient(async (c) => - { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a UNIQUE)"); - await c.execute("INSERT INTO t VALUES (1), (2), (3)"); - + test( + "after error, further statements are not executed", + withClient(async (c) => { const txn = await c.transaction("write"); - const prom1 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); - const prom2 = txn.execute("INSERT INTO t VALUES (4), (5)"); - - await expect(prom1).rejects.toBeLibsqlError(); - await expect(prom2).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - await expect(txn.commit()).rejects.toBeLibsqlError(); - txn.close(); - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(3); - })); + await expect( + txn.batch([ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a UNIQUE)", + "INSERT INTO t VALUES (1), (2), (4)", + "INSERT INTO t VALUES (1)", + "INSERT INTO t VALUES (8), (16)", + ]), + ).rejects.toBeLibsqlError(); + + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); - test("commit empty", withClient(async (c) => { - const txn = await c.transaction("read"); await txn.commit(); - })); - - test("rollback empty", withClient(async (c) => { - const txn = await c.transaction("read"); - await txn.rollback(); - })); - - describe("batch()", () => { - test("as the first operation on transaction", withClient(async (c) => { - const txn = await c.transaction("write"); - - await txn.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - {sql: "INSERT INTO t VALUES (?)", args: [1]}, - {sql: "INSERT INTO t VALUES (?)", args: [2]}, - {sql: "INSERT INTO t VALUES (?)", args: [4]}, - ]); - - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - txn.close(); - })); - - test("as the second operation on transaction", withClient(async (c) => { - const txn = await c.transaction("write"); - - await txn.execute("DROP TABLE IF EXISTS t"); - await txn.batch([ - "CREATE TABLE t (a)", - {sql: "INSERT INTO t VALUES (?)", args: [1]}, - {sql: "INSERT INTO t VALUES (?)", args: [2]}, - {sql: "INSERT INTO t VALUES (?)", args: [4]}, - ]); - - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - txn.close(); - })); - - test("after error, further statements are not executed", withClient(async (c) => { - const txn = await c.transaction("write"); - - await expect(txn.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a UNIQUE)", - "INSERT INTO t VALUES (1), (2), (4)", - "INSERT INTO t VALUES (1)", - "INSERT INTO t VALUES (8), (16)", - ])).rejects.toBeLibsqlError(); - - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - - await txn.commit(); - })); - }); - - (hasHrana2 ? describe : describe.skip)("executeMultiple()", () => { - test("as the first operation on transaction", withClient(async (c) => { - const txn = await c.transaction("write"); + }), + ); + }); + + (hasHrana2 ? describe : describe.skip)("executeMultiple()", () => { + test( + "as the first operation on transaction", + withClient(async (c) => { + const txn = await c.transaction("write"); - await txn.executeMultiple(` + await txn.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4), (8); `); - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(15); - txn.close(); - })); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(15); + txn.close(); + }), + ); - test("as the second operation on transaction", withClient(async (c) => { - const txn = await c.transaction("write"); - await txn.execute("DROP TABLE IF EXISTS t"); - await txn.executeMultiple(` + test( + "as the second operation on transaction", + withClient(async (c) => { + const txn = await c.transaction("write"); + await txn.execute("DROP TABLE IF EXISTS t"); + await txn.executeMultiple(` CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4), (8); `); - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(15); - txn.close(); - })); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(15); + txn.close(); + }), + ); - test("after error, further statements are not executed", withClient(async (c) => { - const txn = await c.transaction("write"); + test( + "after error, further statements are not executed", + withClient(async (c) => { + const txn = await c.transaction("write"); - await expect(txn.executeMultiple(` + await expect( + txn.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a UNIQUE); INSERT INTO t VALUES (1), (2), (4); INSERT INTO t VALUES (1); INSERT INTO t VALUES (8), (16); - `)).rejects.toBeLibsqlError(); + `), + ).rejects.toBeLibsqlError(); - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); - await txn.commit(); - })); - }); + await txn.commit(); + }), + ); + }); }); (hasHrana2 ? describe : describe.skip)("executeMultiple()", () => { - test("multiple statements", withClient(async (c) => { - await c.executeMultiple(` + test( + "multiple statements", + withClient(async (c) => { + await c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4), (8); `); - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(15); - })); + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(15); + }), + ); - test("after an error, statements are not executed", withClient(async (c) => { - await expect(c.executeMultiple(` + test( + "after an error, statements are not executed", + withClient(async (c) => { + await expect( + c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4); INSERT INTO t VALUES (foo()); INSERT INTO t VALUES (8), (16); - `)).rejects.toBeLibsqlError(); - - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - })); - - test("manual transaction control statements", withClient(async (c) => { - await c.executeMultiple(` + `), + ).rejects.toBeLibsqlError(); + + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); + }), + ); + + test( + "manual transaction control statements", + withClient(async (c) => { + await c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); BEGIN; @@ -928,12 +1242,16 @@ describe("transaction()", () => { COMMIT; `); - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(31); - })); + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(31); + }), + ); - test("error rolls back a manual transaction", withClient(async (c) => { - await expect(c.executeMultiple(` + test( + "error rolls back a manual transaction", + withClient(async (c) => { + await expect( + c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (0); @@ -942,68 +1260,86 @@ describe("transaction()", () => { INSERT INTO t VALUES (foo()); INSERT INTO t VALUES (8), (16); COMMIT; - `)).rejects.toBeLibsqlError(); + `), + ).rejects.toBeLibsqlError(); - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - })); + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); }); (hasNetworkErrors ? describe : describe.skip)("network errors", () => { - const testCases = [ - {title: "WebSocket close", sql: ".close_ws"}, - {title: "TCP close", sql: ".close_tcp"}, - ]; - - for (const {title, sql} of testCases) { - test(`${title} in execute()`, withClient(async (c) => { - await expect(c.execute(sql)).rejects.toBeLibsqlError("HRANA_WEBSOCKET_ERROR"); - - expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); - })); - - test(`${title} in transaction()`, withClient(async (c) => { - const txn = await c.transaction("read"); - await expect(txn.execute(sql)).rejects.toBeLibsqlError("HRANA_WEBSOCKET_ERROR"); - await expect(txn.commit()).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - txn.close(); + const testCases = [ + { title: "WebSocket close", sql: ".close_ws" }, + { title: "TCP close", sql: ".close_tcp" }, + ]; + + for (const { title, sql } of testCases) { + test( + `${title} in execute()`, + withClient(async (c) => { + await expect(c.execute(sql)).rejects.toBeLibsqlError( + "HRANA_WEBSOCKET_ERROR", + ); - expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); - })); + expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); + }), + ); - test(`${title} in batch()`, withClient(async (c) => { - await expect(c.batch(["SELECT 42", sql, "SELECT 24"], "read")) - .rejects.toBeLibsqlError("HRANA_WEBSOCKET_ERROR"); + test( + `${title} in transaction()`, + withClient(async (c) => { + const txn = await c.transaction("read"); + await expect(txn.execute(sql)).rejects.toBeLibsqlError( + "HRANA_WEBSOCKET_ERROR", + ); + await expect(txn.commit()).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + txn.close(); - expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); - })); - } + expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); + }), + ); + + test( + `${title} in batch()`, + withClient(async (c) => { + await expect( + c.batch(["SELECT 42", sql, "SELECT 24"], "read"), + ).rejects.toBeLibsqlError("HRANA_WEBSOCKET_ERROR"); + + expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); + }), + ); + } }); (isHttp ? test : test.skip)("custom fetch", async () => { - let fetchCalledCount = 0; - function customFetch(request: Request): Promise { - fetchCalledCount += 1; - return fetch(request); - } - - const c = createClient({...config, fetch: customFetch}); - try { - const rs = await c.execute("SELECT 42"); - expect(rs.rows[0][0]).toStrictEqual(42); - expect(fetchCalledCount).toBeGreaterThan(0); - } finally { - c.close(); - } + let fetchCalledCount = 0; + function customFetch(request: Request): Promise { + fetchCalledCount += 1; + return fetch(request); + } + + const c = createClient({ ...config, fetch: customFetch }); + try { + const rs = await c.execute("SELECT 42"); + expect(rs.rows[0][0]).toStrictEqual(42); + expect(fetchCalledCount).toBeGreaterThan(0); + } finally { + c.close(); + } }); -(isFile ? test : test.skip)('raw error codes', async () => { - const c = createClient(config) - try { - await expect(c.execute('NOT A VALID SQL')).rejects.toThrow( - expect.toBeLibsqlError({ code: 'SQLITE_ERROR', rawCode: 1}) - ) - } finally { - c.close(); - } -}) +(isFile ? test : test.skip)("raw error codes", async () => { + const c = createClient(config); + try { + await expect(c.execute("NOT A VALID SQL")).rejects.toThrow( + expect.toBeLibsqlError({ code: "SQLITE_ERROR", rawCode: 1 }), + ); + } finally { + c.close(); + } +}); diff --git a/packages/libsql-client/src/__tests__/handlers.ts b/packages/libsql-client/src/__tests__/handlers.ts new file mode 100644 index 00000000..51d5ce4e --- /dev/null +++ b/packages/libsql-client/src/__tests__/handlers.ts @@ -0,0 +1,16 @@ +import { http, HttpResponse } from "msw"; + +export const handlers = [ + // Intercept "GET https://example.com/user" requests... + http.get("http://localhost:8080", ({ request }) => { + //console.log("request: ", request); + // print path + console.log("path: ", request.url); + // ...and respond to them using this JSON response. + //return HttpResponse.json({ + //id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', + //firstName: 'John', + //lastName: 'Maverick', + //}) + }), +]; diff --git a/packages/libsql-client/src/__tests__/mocks.ts b/packages/libsql-client/src/__tests__/mocks.ts new file mode 100644 index 00000000..da84961e --- /dev/null +++ b/packages/libsql-client/src/__tests__/mocks.ts @@ -0,0 +1,7 @@ +import { setupServer } from "msw/node"; +import { handlers } from "./handlers"; + +export const server = setupServer(...handlers); +server.events.on("request:start", ({ request }) => { + console.log("Outgoing:", request.method, request.url); +}); From 6df64d3b131fef91884fb96cfe8a503e308bedc0 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 14:53:15 -0400 Subject: [PATCH 10/45] Run prettier on all project --- package-lock.json | 9352 ++++++++--------- .../src/__tests__/client.test.ts | 2421 ++--- .../libsql-client/src/__tests__/handlers.ts | 24 +- packages/libsql-client/src/__tests__/mocks.ts | 2 +- packages/libsql-client/src/http.ts | 79 +- packages/libsql-client/src/migrations.ts | 204 +- packages/libsql-client/src/ws.ts | 86 +- packages/libsql-core/src/api.ts | 41 +- test.ts | 39 +- 9 files changed, 6210 insertions(+), 6038 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41b67498..ae9dbc2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4680 +1,4680 @@ { - "name": "libsql-client-ts", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "workspaces": [ - "packages/libsql-core", - "packages/libsql-client", - "packages/libsql-client-wasm" - ], - "dependencies": { - "husky": "^9.0.11", - "lint-staged": "^15.2.2" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.7", - "@babel/parser": "^7.23.6", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.6", - "dev": true, - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@bundled-es-modules/cookie": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "cookie": "^0.5.0" - } - }, - "node_modules/@bundled-es-modules/statuses": { - "version": "1.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "statuses": "^2.0.1" - } - }, - "node_modules/@inquirer/confirm": { - "version": "3.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^8.2.0", - "@inquirer/type": "^1.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core": { - "version": "8.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.1", - "@inquirer/type": "^1.3.1", - "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.11", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core/node_modules/@types/node": { - "version": "20.12.12", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@inquirer/core/node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type": { - "version": "1.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.21", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@libsql/client": { - "resolved": "packages/libsql-client", - "link": true - }, - "node_modules/@libsql/client-wasm": { - "resolved": "packages/libsql-client-wasm", - "link": true - }, - "node_modules/@libsql/core": { - "resolved": "packages/libsql-core", - "link": true - }, - "node_modules/@libsql/darwin-arm64": { - "version": "0.3.10", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@libsql/hrana-client": { - "version": "0.6.0", - "license": "MIT", - "dependencies": { - "@libsql/isomorphic-fetch": "^0.2.1", - "@libsql/isomorphic-ws": "^0.1.5", - "js-base64": "^3.7.5", - "node-fetch": "^3.3.2" - } - }, - "node_modules/@libsql/isomorphic-fetch": { - "version": "0.2.1", - "license": "MIT" - }, - "node_modules/@libsql/isomorphic-ws": { - "version": "0.1.5", - "license": "MIT", - "dependencies": { - "@types/ws": "^8.5.4", - "ws": "^8.13.0" - } - }, - "node_modules/@libsql/libsql-wasm-experimental": { - "version": "0.0.2", - "license": "Apache-2.0", - "bin": { - "sqlite-wasm": "bin/index.js" - } - }, - "node_modules/@mswjs/cookies": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@mswjs/interceptors": { - "version": "0.29.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.2.1", - "strict-event-emitter": "^0.5.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@neon-rs/load": { - "version": "0.0.4", - "license": "MIT" - }, - "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.11", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/mute-stream": { - "version": "0.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "18.19.8", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/statuses": { - "version": "2.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/wrap-ansi": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.5.10", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-sequence-parser": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.2", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001579", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "dev": true, - "license": "MIT" - }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", - "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "engines": { - "node": ">=18" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.5.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-libc": { - "version": "2.0.2", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.637", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/error-ex": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, - "node_modules/execa": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "node_modules/graphql": { - "version": "16.8.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/headers-polyfill": { - "version": "4.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", - "bin": { - "husky": "bin.mjs" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-node-process": { - "version": "1.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/jest-util": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-base64": { - "version": "3.7.5", - "license": "BSD-3-Clause" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/kleur": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/libsql": { - "version": "0.3.10", - "cpu": [ - "x64", - "arm64", - "wasm32" - ], - "license": "MIT", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "@neon-rs/load": "^0.0.4", - "detect-libc": "2.0.2" - }, - "optionalDependencies": { - "@libsql/darwin-arm64": "0.3.10", - "@libsql/darwin-x64": "0.3.10", - "@libsql/linux-arm64-gnu": "0.3.10", - "@libsql/linux-arm64-musl": "0.3.10", - "@libsql/linux-x64-gnu": "0.3.10", - "@libsql/linux-x64-musl": "0.3.10", - "@libsql/win32-x64-msvc": "0.3.10" - } - }, - "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/lint-staged": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.4.tgz", - "integrity": "sha512-3F9KRQIS2fVDGtCkBp4Bx0jswjX7zUcKx6OF0ZeY1prksUyKPRIIUqZhIUYAstJfvj6i48VFs4dwVIbCYwvTYQ==", - "dependencies": { - "chalk": "5.3.0", - "commander": "12.1.0", - "debug": "4.3.4", - "execa": "8.0.1", - "lilconfig": "3.1.1", - "listr2": "8.2.1", - "micromatch": "4.0.6", - "pidtree": "0.6.0", - "string-argv": "0.3.2", - "yaml": "2.4.2" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/lint-staged/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", - "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.0.0", - "rfdc": "^1.3.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" - }, - "node_modules/listr2/node_modules/string-width": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", - "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/log-update": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", - "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", - "dependencies": { - "ansi-escapes": "^6.2.0", - "cli-cursor": "^4.0.0", - "slice-ansi": "^7.0.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", - "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", - "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "dev": true, - "license": "MIT" - }, - "node_modules/make-dir": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/make-error": { - "version": "1.3.6", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz", - "integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/msw": { - "version": "2.3.0", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@bundled-es-modules/cookie": "^2.0.0", - "@bundled-es-modules/statuses": "^1.0.1", - "@inquirer/confirm": "^3.0.0", - "@mswjs/cookies": "^1.1.0", - "@mswjs/interceptors": "^0.29.0", - "@open-draft/until": "^2.1.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "chalk": "^4.1.2", - "graphql": "^16.8.1", - "headers-polyfill": "^4.0.2", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.2", - "path-to-regexp": "^6.2.0", - "strict-event-emitter": "^0.5.1", - "type-fest": "^4.9.0", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.7.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/msw/node_modules/type-fest": { - "version": "4.18.2", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mute-stream": { - "version": "1.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.14", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/outvariant": { - "version": "1.4.2", - "dev": true, - "license": "MIT" - }, - "node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "6.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.4", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" - }, - "node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "0.14.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/strict-event-emitter": { - "version": "0.5.1", - "dev": true, - "license": "MIT" - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "29.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/type-detect": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedoc": { - "version": "0.23.28", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.2.12", - "minimatch": "^7.1.3", - "shiki": "^0.14.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 14.14" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "7.4.6", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typescript": { - "version": "4.9.5", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/walker": { - "version": "1.0.8", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.2", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ws": { - "version": "8.16.0", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true + "name": "libsql-client-ts", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "workspaces": [ + "packages/libsql-core", + "packages/libsql-client", + "packages/libsql-client-wasm" + ], + "dependencies": { + "husky": "^9.0.11", + "lint-staged": "^15.2.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@inquirer/confirm": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^8.2.0", + "@inquirer/type": "^1.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "8.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.1", + "@inquirer/type": "^1.3.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^20.12.11", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "20.12.12", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@libsql/client": { + "resolved": "packages/libsql-client", + "link": true + }, + "node_modules/@libsql/client-wasm": { + "resolved": "packages/libsql-client-wasm", + "link": true + }, + "node_modules/@libsql/core": { + "resolved": "packages/libsql-core", + "link": true + }, + "node_modules/@libsql/darwin-arm64": { + "version": "0.3.10", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/hrana-client": { + "version": "0.6.0", + "license": "MIT", + "dependencies": { + "@libsql/isomorphic-fetch": "^0.2.1", + "@libsql/isomorphic-ws": "^0.1.5", + "js-base64": "^3.7.5", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@libsql/isomorphic-fetch": { + "version": "0.2.1", + "license": "MIT" + }, + "node_modules/@libsql/isomorphic-ws": { + "version": "0.1.5", + "license": "MIT", + "dependencies": { + "@types/ws": "^8.5.4", + "ws": "^8.13.0" + } + }, + "node_modules/@libsql/libsql-wasm-experimental": { + "version": "0.0.2", + "license": "Apache-2.0", + "bin": { + "sqlite-wasm": "bin/index.js" + } + }, + "node_modules/@mswjs/cookies": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.29.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@neon-rs/load": { + "version": "0.0.4", + "license": "MIT" + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.11", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.19.8", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.2", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001579", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.637", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/graphql": { + "version": "16.8.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-util": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-base64": { + "version": "3.7.5", + "license": "BSD-3-Clause" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/kleur": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/libsql": { + "version": "0.3.10", + "cpu": [ + "x64", + "arm64", + "wasm32" + ], + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "@neon-rs/load": "^0.0.4", + "detect-libc": "2.0.2" + }, + "optionalDependencies": { + "@libsql/darwin-arm64": "0.3.10", + "@libsql/darwin-x64": "0.3.10", + "@libsql/linux-arm64-gnu": "0.3.10", + "@libsql/linux-arm64-musl": "0.3.10", + "@libsql/linux-x64-gnu": "0.3.10", + "@libsql/linux-x64-musl": "0.3.10", + "@libsql/win32-x64-msvc": "0.3.10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/lint-staged": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.4.tgz", + "integrity": "sha512-3F9KRQIS2fVDGtCkBp4Bx0jswjX7zUcKx6OF0ZeY1prksUyKPRIIUqZhIUYAstJfvj6i48VFs4dwVIbCYwvTYQ==", + "dependencies": { + "chalk": "5.3.0", + "commander": "12.1.0", + "debug": "4.3.4", + "execa": "8.0.1", + "lilconfig": "3.1.1", + "listr2": "8.2.1", + "micromatch": "4.0.6", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.4.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", + "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.0.0", + "rfdc": "^1.3.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", + "dependencies": { + "ansi-escapes": "^6.2.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "dev": true, + "license": "MIT" + }, + "node_modules/make-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz", + "integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/msw": { + "version": "2.3.0", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@inquirer/confirm": "^3.0.0", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.29.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.7.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.18.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.14", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/outvariant": { + "version": "1.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "6.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" + }, + "node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "0.14.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "dev": true, + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedoc": { + "version": "0.23.28", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.2.12", + "minimatch": "^7.1.3", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "7.4.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/walker": { + "version": "1.0.8", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.2", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.16.0", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/libsql-client": { + "name": "@libsql/client", + "version": "0.6.0", + "license": "MIT", + "dependencies": { + "@libsql/core": "^0.6.0", + "@libsql/hrana-client": "^0.6.0", + "js-base64": "^3.7.5", + "libsql": "^0.3.10" + }, + "devDependencies": { + "@types/jest": "^29.2.5", + "@types/node": "^18.15.5", + "husky": "^9.0.11", + "jest": "^29.3.1", + "lint-staged": "^15.2.2", + "msw": "^2.3.0", + "prettier": "3.2.5", + "ts-jest": "^29.0.5", + "typedoc": "^0.23.28", + "typescript": "^4.9.4" + } + }, + "packages/libsql-client-wasm": { + "name": "@libsql/client-wasm", + "version": "0.6.0", + "bundleDependencies": [ + "@libsql/libsql-wasm-experimental" + ], + "license": "MIT", + "dependencies": { + "@libsql/core": "0.6.0", + "@libsql/libsql-wasm-experimental": "^0.0.2", + "js-base64": "^3.7.5" + }, + "devDependencies": { + "@types/jest": "^29.2.5", + "jest": "^29.3.1", + "ts-jest": "^29.0.5", + "typedoc": "^0.23.28", + "typescript": "^4.9.4" + } + }, + "packages/libsql-core": { + "name": "@libsql/core", + "version": "0.6.0", + "license": "MIT", + "dependencies": { + "js-base64": "^3.7.5" + }, + "devDependencies": { + "@types/jest": "^29.2.5", + "@types/node": "^18.15.5", + "jest": "^29.3.1", + "ts-jest": "^29.0.5", + "typedoc": "^0.23.28", + "typescript": "^4.9.4" + } } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "packages/libsql-client": { - "name": "@libsql/client", - "version": "0.6.0", - "license": "MIT", - "dependencies": { - "@libsql/core": "^0.6.0", - "@libsql/hrana-client": "^0.6.0", - "js-base64": "^3.7.5", - "libsql": "^0.3.10" - }, - "devDependencies": { - "@types/jest": "^29.2.5", - "@types/node": "^18.15.5", - "husky": "^9.0.11", - "jest": "^29.3.1", - "lint-staged": "^15.2.2", - "msw": "^2.3.0", - "prettier": "3.2.5", - "ts-jest": "^29.0.5", - "typedoc": "^0.23.28", - "typescript": "^4.9.4" - } - }, - "packages/libsql-client-wasm": { - "name": "@libsql/client-wasm", - "version": "0.6.0", - "bundleDependencies": [ - "@libsql/libsql-wasm-experimental" - ], - "license": "MIT", - "dependencies": { - "@libsql/core": "0.6.0", - "@libsql/libsql-wasm-experimental": "^0.0.2", - "js-base64": "^3.7.5" - }, - "devDependencies": { - "@types/jest": "^29.2.5", - "jest": "^29.3.1", - "ts-jest": "^29.0.5", - "typedoc": "^0.23.28", - "typescript": "^4.9.4" - } - }, - "packages/libsql-core": { - "name": "@libsql/core", - "version": "0.6.0", - "license": "MIT", - "dependencies": { - "js-base64": "^3.7.5" - }, - "devDependencies": { - "@types/jest": "^29.2.5", - "@types/node": "^18.15.5", - "jest": "^29.3.1", - "ts-jest": "^29.0.5", - "typedoc": "^0.23.28", - "typescript": "^4.9.4" - } } - } } diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index d688477c..b1aa3fe8 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -16,17 +16,17 @@ afterEach(() => mswServer.resetHandlers()); afterAll(() => mswServer.close()); const config = { - url: process.env.URL ?? "ws://localhost:8080", - syncUrl: process.env.SYNC_URL, - authToken: process.env.AUTH_TOKEN, + url: process.env.URL ?? "ws://localhost:8080", + syncUrl: process.env.SYNC_URL, + authToken: process.env.AUTH_TOKEN, }; const isWs = - config.url.startsWith("ws:") || - config.url.startsWith("wss:") || - config.url.startsWith("libsql:"); + config.url.startsWith("ws:") || + config.url.startsWith("wss:") || + config.url.startsWith("libsql:"); const isHttp = - config.url.startsWith("http:") || config.url.startsWith("https:"); + config.url.startsWith("http:") || config.url.startsWith("https:"); const isFile = config.url.startsWith("file:"); // This allows us to skip tests based on the Hrana server that we are targeting: @@ -39,1201 +39,1268 @@ const server = process.env.SERVER ?? "test_v3"; const hasHrana2 = server !== "test_v1"; const hasHrana3 = server !== "test_v1" && server !== "test_v2"; const hasNetworkErrors = - isWs && - (server === "test_v1" || server === "test_v2" || server === "test_v3"); + isWs && + (server === "test_v1" || server === "test_v2" || server === "test_v3"); function withClient( - f: (c: libsql.Client) => Promise, - extraConfig: Partial = {}, + f: (c: libsql.Client) => Promise, + extraConfig: Partial = {}, ): () => Promise { - return async () => { - const c = createClient({ ...config, ...extraConfig }); - try { - await f(c); - } finally { - c.close(); - } - }; + return async () => { + const c = createClient({ ...config, ...extraConfig }); + try { + await f(c); + } finally { + c.close(); + } + }; } function withInMemoryClient( - f: (c: libsql.Client) => Promise, + f: (c: libsql.Client) => Promise, ): () => Promise { - return async () => { - const c = createClient({ url: ":memory:" }); - try { - await f(c); - } finally { - c.close(); - } - }; + return async () => { + const c = createClient({ url: ":memory:" }); + try { + await f(c); + } finally { + c.close(); + } + }; } describe("createClient()", () => { - test("URL scheme not supported", () => { - expect(() => createClient({ url: "ftp://localhost" })).toThrow( - expect.toBeLibsqlError("URL_SCHEME_NOT_SUPPORTED", /"ftp:"/), + test("URL scheme not supported", () => { + expect(() => createClient({ url: "ftp://localhost" })).toThrow( + expect.toBeLibsqlError("URL_SCHEME_NOT_SUPPORTED", /"ftp:"/), + ); + }); + + test("URL param not supported", () => { + expect(() => createClient({ url: "ws://localhost?foo=bar" })).toThrow( + expect.toBeLibsqlError("URL_PARAM_NOT_SUPPORTED", /"foo"/), + ); + }); + + test("URL scheme incompatible with ?tls", () => { + const urls = [ + "ws://localhost?tls=1", + "wss://localhost?tls=0", + "http://localhost?tls=1", + "https://localhost?tls=0", + ]; + for (const url of urls) { + expect(() => createClient({ url })).toThrow( + expect.toBeLibsqlError("URL_INVALID", /TLS/), + ); + } + }); + + test("missing port in libsql URL with tls=0", () => { + expect(() => createClient({ url: "libsql://localhost?tls=0" })).toThrow( + expect.toBeLibsqlError("URL_INVALID", /port/), + ); + }); + + test("invalid value of tls query param", () => { + expect(() => + createClient({ url: "libsql://localhost?tls=yes" }), + ).toThrow(expect.toBeLibsqlError("URL_INVALID", /"tls".*"yes"/)); + }); + + test("passing URL instead of config object", () => { + // @ts-expect-error + expect(() => createClient("ws://localhost")).toThrow( + /as object, got string/, + ); + }); + + test("invalid value for `intMode`", () => { + // @ts-expect-error + expect(() => createClient({ ...config, intMode: "foo" })).toThrow( + /"foo"/, + ); + }); + + test("supports in-memory database", () => { + expect(() => createClient({ url: ":memory:" })).not.toThrow(); + }); +}); + +describe("execute()", () => { + test( + "query a single value", + withClient(async (c) => { + const rs = await c.execute("SELECT 42"); + expect(rs.columns.length).toStrictEqual(1); + expect(rs.columnTypes.length).toStrictEqual(1); + expect(rs.rows.length).toStrictEqual(1); + expect(rs.rows[0].length).toStrictEqual(1); + expect(rs.rows[0][0]).toStrictEqual(42); + }), ); - }); - test("URL param not supported", () => { - expect(() => createClient({ url: "ws://localhost?foo=bar" })).toThrow( - expect.toBeLibsqlError("URL_PARAM_NOT_SUPPORTED", /"foo"/), + test( + "query a single row", + withClient(async (c) => { + const rs = await c.execute( + "SELECT 1 AS one, 'two' AS two, 0.5 AS three", + ); + expect(rs.columns).toStrictEqual(["one", "two", "three"]); + expect(rs.columnTypes).toStrictEqual(["", "", ""]); + expect(rs.rows.length).toStrictEqual(1); + + const r = rs.rows[0]; + expect(r.length).toStrictEqual(3); + expect(Array.from(r)).toStrictEqual([1, "two", 0.5]); + expect(Object.entries(r)).toStrictEqual([ + ["one", 1], + ["two", "two"], + ["three", 0.5], + ]); + }), ); - }); - - test("URL scheme incompatible with ?tls", () => { - const urls = [ - "ws://localhost?tls=1", - "wss://localhost?tls=0", - "http://localhost?tls=1", - "https://localhost?tls=0", - ]; - for (const url of urls) { - expect(() => createClient({ url })).toThrow( - expect.toBeLibsqlError("URL_INVALID", /TLS/), - ); - } - }); - test("missing port in libsql URL with tls=0", () => { - expect(() => createClient({ url: "libsql://localhost?tls=0" })).toThrow( - expect.toBeLibsqlError("URL_INVALID", /port/), + test( + "query multiple rows", + withClient(async (c) => { + const rs = await c.execute( + "VALUES (1, 'one'), (2, 'two'), (3, 'three')", + ); + expect(rs.columns.length).toStrictEqual(2); + expect(rs.columnTypes.length).toStrictEqual(2); + expect(rs.rows.length).toStrictEqual(3); + + expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); + expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); + expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); + }), ); - }); - test("invalid value of tls query param", () => { - expect(() => createClient({ url: "libsql://localhost?tls=yes" })).toThrow( - expect.toBeLibsqlError("URL_INVALID", /"tls".*"yes"/), + test( + "statement that produces error", + withClient(async (c) => { + await expect(c.execute("SELECT foobar")).rejects.toBeLibsqlError(); + }), ); - }); - test("passing URL instead of config object", () => { - // @ts-expect-error - expect(() => createClient("ws://localhost")).toThrow( - /as object, got string/, + test( + "rowsAffected with INSERT", + withClient(async (c) => { + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + const rs = await c.execute("INSERT INTO t VALUES (1), (2)"); + expect(rs.rowsAffected).toStrictEqual(2); + }), ); - }); - test("invalid value for `intMode`", () => { - // @ts-expect-error - expect(() => createClient({ ...config, intMode: "foo" })).toThrow(/"foo"/); - }); + test( + "rowsAffected with DELETE", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES (1), (2), (3), (4), (5)", + ], + "write", + ); + const rs = await c.execute("DELETE FROM t WHERE a >= 3"); + expect(rs.rowsAffected).toStrictEqual(3); + }), + ); - test("supports in-memory database", () => { - expect(() => createClient({ url: ":memory:" })).not.toThrow(); - }); -}); + test( + "lastInsertRowid with INSERT", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES ('one'), ('two')", + ], + "write", + ); + const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); + expect(insertRs.lastInsertRowid).not.toBeUndefined(); + const selectRs = await c.execute({ + sql: "SELECT a FROM t WHERE ROWID = ?", + args: [insertRs.lastInsertRowid!], + }); + expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); + }), + ); -describe("execute()", () => { - test( - "query a single value", - withClient(async (c) => { - const rs = await c.execute("SELECT 42"); - expect(rs.columns.length).toStrictEqual(1); - expect(rs.columnTypes.length).toStrictEqual(1); - expect(rs.rows.length).toStrictEqual(1); - expect(rs.rows[0].length).toStrictEqual(1); - expect(rs.rows[0][0]).toStrictEqual(42); - }), - ); - - test( - "query a single row", - withClient(async (c) => { - const rs = await c.execute("SELECT 1 AS one, 'two' AS two, 0.5 AS three"); - expect(rs.columns).toStrictEqual(["one", "two", "three"]); - expect(rs.columnTypes).toStrictEqual(["", "", ""]); - expect(rs.rows.length).toStrictEqual(1); - - const r = rs.rows[0]; - expect(r.length).toStrictEqual(3); - expect(Array.from(r)).toStrictEqual([1, "two", 0.5]); - expect(Object.entries(r)).toStrictEqual([ - ["one", 1], - ["two", "two"], - ["three", 0.5], - ]); - }), - ); - - test( - "query multiple rows", - withClient(async (c) => { - const rs = await c.execute("VALUES (1, 'one'), (2, 'two'), (3, 'three')"); - expect(rs.columns.length).toStrictEqual(2); - expect(rs.columnTypes.length).toStrictEqual(2); - expect(rs.rows.length).toStrictEqual(3); - - expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); - expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); - expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); - }), - ); - - test( - "statement that produces error", - withClient(async (c) => { - await expect(c.execute("SELECT foobar")).rejects.toBeLibsqlError(); - }), - ); - - test( - "rowsAffected with INSERT", - withClient(async (c) => { - await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); - const rs = await c.execute("INSERT INTO t VALUES (1), (2)"); - expect(rs.rowsAffected).toStrictEqual(2); - }), - ); - - test( - "rowsAffected with DELETE", - withClient(async (c) => { - await c.batch( - [ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES (1), (2), (3), (4), (5)", - ], - "write", - ); - const rs = await c.execute("DELETE FROM t WHERE a >= 3"); - expect(rs.rowsAffected).toStrictEqual(3); - }), - ); - - test( - "lastInsertRowid with INSERT", - withClient(async (c) => { - await c.batch( - [ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES ('one'), ('two')", - ], - "write", - ); - const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); - expect(insertRs.lastInsertRowid).not.toBeUndefined(); - const selectRs = await c.execute({ - sql: "SELECT a FROM t WHERE ROWID = ?", - args: [insertRs.lastInsertRowid!], - }); - expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); - }), - ); - - test( - "rows from INSERT RETURNING", - withClient(async (c) => { - await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); - - const rs = await c.execute( - "INSERT INTO t VALUES (1) RETURNING 42 AS x, 'foo' AS y", - ); - expect(rs.columns).toStrictEqual(["x", "y"]); - expect(rs.columnTypes).toStrictEqual(["", ""]); - expect(rs.rows.length).toStrictEqual(1); - expect(Array.from(rs.rows[0])).toStrictEqual([42, "foo"]); - }), - ); - - (hasHrana2 ? test : test.skip)( - "rowsAffected with WITH INSERT", - withClient(async (c) => { - await c.batch( - [ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES (1), (2), (3)", - ], - "write", - ); - - const rs = await c.execute(` + test( + "rows from INSERT RETURNING", + withClient(async (c) => { + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + + const rs = await c.execute( + "INSERT INTO t VALUES (1) RETURNING 42 AS x, 'foo' AS y", + ); + expect(rs.columns).toStrictEqual(["x", "y"]); + expect(rs.columnTypes).toStrictEqual(["", ""]); + expect(rs.rows.length).toStrictEqual(1); + expect(Array.from(rs.rows[0])).toStrictEqual([42, "foo"]); + }), + ); + + (hasHrana2 ? test : test.skip)( + "rowsAffected with WITH INSERT", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES (1), (2), (3)", + ], + "write", + ); + + const rs = await c.execute(` WITH x(a) AS (SELECT 2*a FROM t) INSERT INTO t SELECT a+1 FROM x `); - expect(rs.rowsAffected).toStrictEqual(3); - }), - ); - - test( - "query a single value using an in memory database", - withInMemoryClient(async (c) => { - await c.batch( - [ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES ('one'), ('two')", - ], - "write", - ); - const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); - expect(insertRs.lastInsertRowid).not.toBeUndefined(); - const selectRs = await c.execute({ - sql: "SELECT a FROM t WHERE ROWID = ?", - args: [insertRs.lastInsertRowid!], - }); - expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); - }), - ); -}); - -describe("values", () => { - function testRoundtrip( - name: string, - passed: libsql.InValue, - expected: libsql.Value, - intMode?: libsql.IntMode, - ): void { - test( - name, - withClient( - async (c) => { - const rs = await c.execute({ sql: "SELECT ?", args: [passed] }); - expect(rs.rows[0][0]).toStrictEqual(expected); - }, - { intMode }, - ), + expect(rs.rowsAffected).toStrictEqual(3); + }), ); - } - - function testRoundtripError( - name: string, - passed: libsql.InValue, - expectedError: unknown, - intMode?: libsql.IntMode, - ): void { + test( - name, - withClient( - async (c) => { - await expect( - c.execute({ - sql: "SELECT ?", - args: [passed], - }), - ).rejects.toBeInstanceOf(expectedError); - }, - { intMode }, - ), + "query a single value using an in memory database", + withInMemoryClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES ('one'), ('two')", + ], + "write", + ); + const insertRs = await c.execute("INSERT INTO t VALUES ('three')"); + expect(insertRs.lastInsertRowid).not.toBeUndefined(); + const selectRs = await c.execute({ + sql: "SELECT a FROM t WHERE ROWID = ?", + args: [insertRs.lastInsertRowid!], + }); + expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); + }), ); - } - - testRoundtrip("string", "boomerang", "boomerang"); - testRoundtrip("string with weird characters", "a\n\r\t ", "a\n\r\t "); - testRoundtrip( - "string with unicode", - "žluťoučký kůň úpěl ďábelské ódy", - "žluťoučký kůň úpěl ďábelské ódy", - ); - - describe("number", () => { - const intModes: Array = ["number", "bigint", "string"]; - for (const intMode of intModes) { - testRoundtrip("zero", 0, 0, intMode); - testRoundtrip("integer", -2023, -2023, intMode); - testRoundtrip("float", 12.345, 12.345, intMode); - testRoundtrip("large positive float", 1e18, 1e18, intMode); - testRoundtrip("large negative float", -1e18, -1e18, intMode); - testRoundtrip("MAX_VALUE", Number.MAX_VALUE, Number.MAX_VALUE, intMode); - testRoundtrip( - "-MAX_VALUE", - -Number.MAX_VALUE, - -Number.MAX_VALUE, - intMode, - ); - testRoundtrip("MIN_VALUE", Number.MIN_VALUE, Number.MIN_VALUE, intMode); +}); + +describe("values", () => { + function testRoundtrip( + name: string, + passed: libsql.InValue, + expected: libsql.Value, + intMode?: libsql.IntMode, + ): void { + test( + name, + withClient( + async (c) => { + const rs = await c.execute({ + sql: "SELECT ?", + args: [passed], + }); + expect(rs.rows[0][0]).toStrictEqual(expected); + }, + { intMode }, + ), + ); } - }); - - describe("bigint", () => { - describe("'number' int mode", () => { - testRoundtrip("zero integer", 0n, 0, "number"); - testRoundtrip("small integer", -42n, -42, "number"); - testRoundtrip( - "largest safe integer", - 9007199254740991n, - 9007199254740991, - "number", - ); - testRoundtripError( - "smallest unsafe integer", - 9007199254740992n, - RangeError, - "number", - ); - testRoundtripError( - "large unsafe integer", - -1152921504594532842n, - RangeError, - "number", - ); - }); - describe("'bigint' int mode", () => { - testRoundtrip("zero integer", 0n, 0n, "bigint"); - testRoundtrip("small integer", -42n, -42n, "bigint"); - testRoundtrip( - "large positive integer", - 1152921504608088318n, - 1152921504608088318n, - "bigint", - ); - testRoundtrip( - "large negative integer", - -1152921504594532842n, - -1152921504594532842n, - "bigint", - ); - testRoundtrip( - "largest positive integer", - 9223372036854775807n, - 9223372036854775807n, - "bigint", - ); - testRoundtrip( - "largest negative integer", - -9223372036854775808n, - -9223372036854775808n, - "bigint", - ); + function testRoundtripError( + name: string, + passed: libsql.InValue, + expectedError: unknown, + intMode?: libsql.IntMode, + ): void { + test( + name, + withClient( + async (c) => { + await expect( + c.execute({ + sql: "SELECT ?", + args: [passed], + }), + ).rejects.toBeInstanceOf(expectedError); + }, + { intMode }, + ), + ); + } + + testRoundtrip("string", "boomerang", "boomerang"); + testRoundtrip("string with weird characters", "a\n\r\t ", "a\n\r\t "); + testRoundtrip( + "string with unicode", + "žluťoučký kůň úpěl ďábelské ódy", + "žluťoučký kůň úpěl ďábelské ódy", + ); + + describe("number", () => { + const intModes: Array = ["number", "bigint", "string"]; + for (const intMode of intModes) { + testRoundtrip("zero", 0, 0, intMode); + testRoundtrip("integer", -2023, -2023, intMode); + testRoundtrip("float", 12.345, 12.345, intMode); + testRoundtrip("large positive float", 1e18, 1e18, intMode); + testRoundtrip("large negative float", -1e18, -1e18, intMode); + testRoundtrip( + "MAX_VALUE", + Number.MAX_VALUE, + Number.MAX_VALUE, + intMode, + ); + testRoundtrip( + "-MAX_VALUE", + -Number.MAX_VALUE, + -Number.MAX_VALUE, + intMode, + ); + testRoundtrip( + "MIN_VALUE", + Number.MIN_VALUE, + Number.MIN_VALUE, + intMode, + ); + } }); - describe("'string' int mode", () => { - testRoundtrip("zero integer", 0n, "0", "string"); - testRoundtrip("small integer", -42n, "-42", "string"); - testRoundtrip( - "large positive integer", - 1152921504608088318n, - "1152921504608088318", - "string", - ); - testRoundtrip( - "large negative integer", - -1152921504594532842n, - "-1152921504594532842", - "string", - ); - testRoundtrip( - "largest positive integer", - 9223372036854775807n, - "9223372036854775807", - "string", - ); - testRoundtrip( - "largest negative integer", - -9223372036854775808n, - "-9223372036854775808", - "string", - ); + describe("bigint", () => { + describe("'number' int mode", () => { + testRoundtrip("zero integer", 0n, 0, "number"); + testRoundtrip("small integer", -42n, -42, "number"); + testRoundtrip( + "largest safe integer", + 9007199254740991n, + 9007199254740991, + "number", + ); + testRoundtripError( + "smallest unsafe integer", + 9007199254740992n, + RangeError, + "number", + ); + testRoundtripError( + "large unsafe integer", + -1152921504594532842n, + RangeError, + "number", + ); + }); + + describe("'bigint' int mode", () => { + testRoundtrip("zero integer", 0n, 0n, "bigint"); + testRoundtrip("small integer", -42n, -42n, "bigint"); + testRoundtrip( + "large positive integer", + 1152921504608088318n, + 1152921504608088318n, + "bigint", + ); + testRoundtrip( + "large negative integer", + -1152921504594532842n, + -1152921504594532842n, + "bigint", + ); + testRoundtrip( + "largest positive integer", + 9223372036854775807n, + 9223372036854775807n, + "bigint", + ); + testRoundtrip( + "largest negative integer", + -9223372036854775808n, + -9223372036854775808n, + "bigint", + ); + }); + + describe("'string' int mode", () => { + testRoundtrip("zero integer", 0n, "0", "string"); + testRoundtrip("small integer", -42n, "-42", "string"); + testRoundtrip( + "large positive integer", + 1152921504608088318n, + "1152921504608088318", + "string", + ); + testRoundtrip( + "large negative integer", + -1152921504594532842n, + "-1152921504594532842", + "string", + ); + testRoundtrip( + "largest positive integer", + 9223372036854775807n, + "9223372036854775807", + "string", + ); + testRoundtrip( + "largest negative integer", + -9223372036854775808n, + "-9223372036854775808", + "string", + ); + }); }); - }); - - const buf = new ArrayBuffer(256); - const array = new Uint8Array(buf); - for (let i = 0; i < 256; ++i) { - array[i] = i ^ 0xab; - } - testRoundtrip("ArrayBuffer", buf, buf); - testRoundtrip("Uint8Array", array, buf); - - testRoundtrip("null", null, null); - testRoundtrip("true", true, 1n, "bigint"); - testRoundtrip("false", false, 0n, "bigint"); - testRoundtrip("true", true, 1, "number"); - testRoundtrip("false", false, 0, "number"); - testRoundtrip("true", true, "1", "string"); - testRoundtrip("false", false, "0", "string"); - testRoundtrip("true", true, 1); - testRoundtrip("false", false, 0); - - testRoundtrip( - "Date", - new Date("2023-01-02T12:34:56Z"), - 1672662896000, - "bigint", - ); - - // @ts-expect-error - testRoundtripError("undefined produces error", undefined, TypeError); - testRoundtripError("NaN produces error", NaN, RangeError); - testRoundtripError("Infinity produces error", Infinity, RangeError); - testRoundtripError( - "large bigint produces error", - -1267650600228229401496703205376n, - RangeError, - ); - - test( - "max 64-bit bigint", - withClient(async (c) => { - const rs = await c.execute({ - sql: "SELECT ?||''", - args: [9223372036854775807n], - }); - expect(rs.rows[0][0]).toStrictEqual("9223372036854775807"); - }), - ); - - test( - "min 64-bit bigint", - withClient(async (c) => { - const rs = await c.execute({ - sql: "SELECT ?||''", - args: [-9223372036854775808n], - }); - expect(rs.rows[0][0]).toStrictEqual("-9223372036854775808"); - }), - ); + + const buf = new ArrayBuffer(256); + const array = new Uint8Array(buf); + for (let i = 0; i < 256; ++i) { + array[i] = i ^ 0xab; + } + testRoundtrip("ArrayBuffer", buf, buf); + testRoundtrip("Uint8Array", array, buf); + + testRoundtrip("null", null, null); + testRoundtrip("true", true, 1n, "bigint"); + testRoundtrip("false", false, 0n, "bigint"); + testRoundtrip("true", true, 1, "number"); + testRoundtrip("false", false, 0, "number"); + testRoundtrip("true", true, "1", "string"); + testRoundtrip("false", false, "0", "string"); + testRoundtrip("true", true, 1); + testRoundtrip("false", false, 0); + + testRoundtrip( + "Date", + new Date("2023-01-02T12:34:56Z"), + 1672662896000, + "bigint", + ); + + // @ts-expect-error + testRoundtripError("undefined produces error", undefined, TypeError); + testRoundtripError("NaN produces error", NaN, RangeError); + testRoundtripError("Infinity produces error", Infinity, RangeError); + testRoundtripError( + "large bigint produces error", + -1267650600228229401496703205376n, + RangeError, + ); + + test( + "max 64-bit bigint", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?||''", + args: [9223372036854775807n], + }); + expect(rs.rows[0][0]).toStrictEqual("9223372036854775807"); + }), + ); + + test( + "min 64-bit bigint", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?||''", + args: [-9223372036854775808n], + }); + expect(rs.rows[0][0]).toStrictEqual("-9223372036854775808"); + }), + ); }); describe("ResultSet.toJSON()", () => { - test( - "simple result set", - withClient(async (c) => { - const rs = await c.execute("SELECT 1 AS a"); - const json = rs.toJSON(); - expect( - json["lastInsertRowid"] === null || json["lastInsertRowid"] === "0", - ).toBe(true); - expect(json["columns"]).toStrictEqual(["a"]); - expect(json["columnTypes"]).toStrictEqual([""]); - expect(json["rows"]).toStrictEqual([[1]]); - expect(json["rowsAffected"]).toStrictEqual(0); - - const str = JSON.stringify(rs); - expect( - str === - '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":null}' || - str === - '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":"0"}', - ).toBe(true); - }), - ); - - test( - "lastInsertRowid", - withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (id INTEGER PRIMARY KEY NOT NULL)"); - const rs = await c.execute("INSERT INTO t VALUES (12345)"); - expect(rs.toJSON()).toStrictEqual({ - columns: [], - columnTypes: [], - rows: [], - rowsAffected: 1, - lastInsertRowid: "12345", - }); - }), - ); - - test( - "computed values", - withClient(async (c) => { - const rs = await c.execute( - "SELECT 42 AS integer, 0.5 AS float, NULL AS \"null\", 'foo' AS text, X'626172' AS blob", - ); - const json = rs.toJSON(); - expect(json["columns"]).toStrictEqual([ - "integer", - "float", - "null", - "text", - "blob", - ]); - expect(json["columnTypes"]).toStrictEqual(["", "", "", "", ""]); - expect(json["rows"]).toStrictEqual([[42, 0.5, null, "foo", "YmFy"]]); - }), - ); - - (hasHrana2 ? test : test.skip)( - "row values", - withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (i INTEGER, f FLOAT, t TEXT, b BLOB)"); - await c.execute("INSERT INTO t VALUES (42, 0.5, 'foo', X'626172')"); - const rs = await c.execute("SELECT i, f, t, b FROM t LIMIT 1"); - const json = rs.toJSON(); - expect(json["columns"]).toStrictEqual(["i", "f", "t", "b"]); - expect(json["columnTypes"]).toStrictEqual([ - "INTEGER", - "FLOAT", - "TEXT", - "BLOB", - ]); - expect(json["rows"]).toStrictEqual([[42, 0.5, "foo", "YmFy"]]); - }), - ); - - test( - "bigint row value", - withClient( - async (c) => { - const rs = await c.execute("SELECT 42"); - const json = rs.toJSON(); - expect(json["rows"]).toStrictEqual([["42"]]); - }, - { intMode: "bigint" }, - ), - ); -}); + test( + "simple result set", + withClient(async (c) => { + const rs = await c.execute("SELECT 1 AS a"); + const json = rs.toJSON(); + expect( + json["lastInsertRowid"] === null || + json["lastInsertRowid"] === "0", + ).toBe(true); + expect(json["columns"]).toStrictEqual(["a"]); + expect(json["columnTypes"]).toStrictEqual([""]); + expect(json["rows"]).toStrictEqual([[1]]); + expect(json["rowsAffected"]).toStrictEqual(0); + + const str = JSON.stringify(rs); + expect( + str === + '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":null}' || + str === + '{"columns":["a"],"columnTypes":[""],"rows":[[1]],"rowsAffected":0,"lastInsertRowid":"0"}', + ).toBe(true); + }), + ); -describe("arguments", () => { - test( - "? arguments", - withClient(async (c) => { - const rs = await c.execute({ - sql: "SELECT ?, ?", - args: ["one", "two"], - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["one", "two"]); - }), - ); - - (!isFile ? test : test.skip)( - "?NNN arguments", - withClient(async (c) => { - const rs = await c.execute({ - sql: "SELECT ?2, ?3, ?1", - args: ["one", "two", "three"], - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "three", "one"]); - }), - ); - - (!isFile ? test : test.skip)( - "?NNN arguments with holes", - withClient(async (c) => { - const rs = await c.execute({ - sql: "SELECT ?3, ?1", - args: ["one", "two", "three"], - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["three", "one"]); - }), - ); - - (!isFile ? test : test.skip)( - "?NNN and ? arguments", - withClient(async (c) => { - const rs = await c.execute({ - sql: "SELECT ?2, ?, ?3", - args: ["one", "two", "three"], - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "three", "three"]); - }), - ); - - for (const sign of [":", "@", "$"]) { test( - `${sign}AAAA arguments`, - withClient(async (c) => { - const rs = await c.execute({ - sql: `SELECT ${sign}b, ${sign}a`, - args: { a: "one", [`${sign}b`]: "two" }, - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one"]); - }), + "lastInsertRowid", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (id INTEGER PRIMARY KEY NOT NULL)"); + const rs = await c.execute("INSERT INTO t VALUES (12345)"); + expect(rs.toJSON()).toStrictEqual({ + columns: [], + columnTypes: [], + rows: [], + rowsAffected: 1, + lastInsertRowid: "12345", + }); + }), ); test( - `${sign}AAAA arguments used multiple times`, - withClient(async (c) => { - const rs = await c.execute({ - sql: `SELECT ${sign}b, ${sign}a, ${sign}b || ${sign}a`, - args: { a: "one", [`${sign}b`]: "two" }, - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one", "twoone"]); - }), + "computed values", + withClient(async (c) => { + const rs = await c.execute( + "SELECT 42 AS integer, 0.5 AS float, NULL AS \"null\", 'foo' AS text, X'626172' AS blob", + ); + const json = rs.toJSON(); + expect(json["columns"]).toStrictEqual([ + "integer", + "float", + "null", + "text", + "blob", + ]); + expect(json["columnTypes"]).toStrictEqual(["", "", "", "", ""]); + expect(json["rows"]).toStrictEqual([ + [42, 0.5, null, "foo", "YmFy"], + ]); + }), + ); + + (hasHrana2 ? test : test.skip)( + "row values", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute( + "CREATE TABLE t (i INTEGER, f FLOAT, t TEXT, b BLOB)", + ); + await c.execute("INSERT INTO t VALUES (42, 0.5, 'foo', X'626172')"); + const rs = await c.execute("SELECT i, f, t, b FROM t LIMIT 1"); + const json = rs.toJSON(); + expect(json["columns"]).toStrictEqual(["i", "f", "t", "b"]); + expect(json["columnTypes"]).toStrictEqual([ + "INTEGER", + "FLOAT", + "TEXT", + "BLOB", + ]); + expect(json["rows"]).toStrictEqual([[42, 0.5, "foo", "YmFy"]]); + }), ); test( - `${sign}AAAA arguments and ?NNN arguments`, - withClient(async (c) => { - const rs = await c.execute({ - sql: `SELECT ${sign}b, ${sign}a, ?1`, - args: { a: "one", [`${sign}b`]: "two" }, - }); - expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one", "two"]); - }), + "bigint row value", + withClient( + async (c) => { + const rs = await c.execute("SELECT 42"); + const json = rs.toJSON(); + expect(json["rows"]).toStrictEqual([["42"]]); + }, + { intMode: "bigint" }, + ), ); - } +}); + +describe("arguments", () => { + test( + "? arguments", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?, ?", + args: ["one", "two"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual(["one", "two"]); + }), + ); + + (!isFile ? test : test.skip)( + "?NNN arguments", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?2, ?3, ?1", + args: ["one", "two", "three"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual([ + "two", + "three", + "one", + ]); + }), + ); + + (!isFile ? test : test.skip)( + "?NNN arguments with holes", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?3, ?1", + args: ["one", "two", "three"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual(["three", "one"]); + }), + ); + + (!isFile ? test : test.skip)( + "?NNN and ? arguments", + withClient(async (c) => { + const rs = await c.execute({ + sql: "SELECT ?2, ?, ?3", + args: ["one", "two", "three"], + }); + expect(Array.from(rs.rows[0])).toStrictEqual([ + "two", + "three", + "three", + ]); + }), + ); + + for (const sign of [":", "@", "$"]) { + test( + `${sign}AAAA arguments`, + withClient(async (c) => { + const rs = await c.execute({ + sql: `SELECT ${sign}b, ${sign}a`, + args: { a: "one", [`${sign}b`]: "two" }, + }); + expect(Array.from(rs.rows[0])).toStrictEqual(["two", "one"]); + }), + ); + + test( + `${sign}AAAA arguments used multiple times`, + withClient(async (c) => { + const rs = await c.execute({ + sql: `SELECT ${sign}b, ${sign}a, ${sign}b || ${sign}a`, + args: { a: "one", [`${sign}b`]: "two" }, + }); + expect(Array.from(rs.rows[0])).toStrictEqual([ + "two", + "one", + "twoone", + ]); + }), + ); + + test( + `${sign}AAAA arguments and ?NNN arguments`, + withClient(async (c) => { + const rs = await c.execute({ + sql: `SELECT ${sign}b, ${sign}a, ?1`, + args: { a: "one", [`${sign}b`]: "two" }, + }); + expect(Array.from(rs.rows[0])).toStrictEqual([ + "two", + "one", + "two", + ]); + }), + ); + } }); describe("batch()", () => { - test( - "multiple queries", - withClient(async (c) => { - const rss = await c.batch( - [ - "SELECT 1+1", - "SELECT 1 AS one, 2 AS two", - { sql: "SELECT ?", args: ["boomerang"] }, - { sql: "VALUES (?), (?)", args: ["big", "ben"] }, - ], - "read", - ); - - expect(rss.length).toStrictEqual(4); - const [rs0, rs1, rs2, rs3] = rss; - - expect(rs0.rows.length).toStrictEqual(1); - expect(Array.from(rs0.rows[0])).toStrictEqual([2]); - - expect(rs1.rows.length).toStrictEqual(1); - expect(Array.from(rs1.rows[0])).toStrictEqual([1, 2]); - - expect(rs2.rows.length).toStrictEqual(1); - expect(Array.from(rs2.rows[0])).toStrictEqual(["boomerang"]); - - expect(rs3.rows.length).toStrictEqual(2); - expect(Array.from(rs3.rows[0])).toStrictEqual(["big"]); - expect(Array.from(rs3.rows[1])).toStrictEqual(["ben"]); - }), - ); - - test( - "statements are executed sequentially", - withClient(async (c) => { - const rss = await c.batch( - [ - /* 0 */ "DROP TABLE IF EXISTS t", - /* 1 */ "CREATE TABLE t (a, b)", - /* 2 */ "INSERT INTO t VALUES (1, 'one')", - /* 3 */ "SELECT * FROM t ORDER BY a", - /* 4 */ "INSERT INTO t VALUES (2, 'two')", - /* 5 */ "SELECT * FROM t ORDER BY a", - /* 6 */ "DROP TABLE t", - ], - "write", - ); - - expect(rss.length).toStrictEqual(7); - expect(rss[3].rows).toEqual([{ a: 1, b: "one" }]); - expect(rss[5].rows).toEqual([ - { a: 1, b: "one" }, - { a: 2, b: "two" }, - ]); - }), - ); - - test( - "statements are executed in a transaction", - withClient(async (c) => { - await c.batch( - [ - "DROP TABLE IF EXISTS t1", - "DROP TABLE IF EXISTS t2", - "CREATE TABLE t1 (a)", - "CREATE TABLE t2 (a)", - ], - "write", - ); - - const n = 100; - const promises = []; - for (let i = 0; i < n; ++i) { - const ii = i; - promises.push( - (async () => { + test( + "multiple queries", + withClient(async (c) => { const rss = await c.batch( - [ - { sql: "INSERT INTO t1 VALUES (?)", args: [ii] }, - { sql: "INSERT INTO t2 VALUES (?)", args: [ii * 10] }, - "SELECT SUM(a) FROM t1", - "SELECT SUM(a) FROM t2", - ], - "write", + [ + "SELECT 1+1", + "SELECT 1 AS one, 2 AS two", + { sql: "SELECT ?", args: ["boomerang"] }, + { sql: "VALUES (?), (?)", args: ["big", "ben"] }, + ], + "read", ); - const sum1 = rss[2].rows[0][0] as number; - const sum2 = rss[3].rows[0][0] as number; - expect(sum2).toStrictEqual(sum1 * 10); - })(), - ); - } - await Promise.all(promises); - - const rs1 = await c.execute("SELECT SUM(a) FROM t1"); - expect(rs1.rows[0][0]).toStrictEqual((n * (n - 1)) / 2); - const rs2 = await c.execute("SELECT SUM(a) FROM t2"); - expect(rs2.rows[0][0]).toStrictEqual(((n * (n - 1)) / 2) * 10); - }), - 10000, - ); - - test( - "error in batch", - withClient(async (c) => { - await expect( - c.batch(["SELECT 1+1", "SELECT foobar"], "read"), - ).rejects.toBeLibsqlError(); - }), - ); - - test( - "error in batch rolls back transaction", - withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a)"); - await c.execute("INSERT INTO t VALUES ('one')"); - await expect( - c.batch( - [ - "INSERT INTO t VALUES ('two')", - "SELECT foobar", - "INSERT INTO t VALUES ('three')", - ], - "write", - ), - ).rejects.toBeLibsqlError(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(1); - }), - ); - - test( - "batch with a lot of different statements", - withClient(async (c) => { - const stmts = []; - for (let i = 0; i < 1000; ++i) { - stmts.push(`SELECT ${i}`); - } - const rss = await c.batch(stmts, "read"); - for (let i = 0; i < stmts.length; ++i) { - expect(rss[i].rows[0][0]).toStrictEqual(i); - } - }), - ); - - test( - "batch with a lot of the same statements", - withClient(async (c) => { - const n = 20; - const m = 200; - - const stmts = []; - for (let i = 0; i < n; ++i) { - for (let j = 0; j < m; ++j) { - stmts.push({ sql: `SELECT ?, ${j}`, args: [i] }); - } - } - - const rss = await c.batch(stmts, "read"); - for (let i = 0; i < n; ++i) { - for (let j = 0; j < m; ++j) { - const rs = rss[i * m + j]; - expect(rs.rows[0][0]).toStrictEqual(i); - expect(rs.rows[0][1]).toStrictEqual(j); - } - } - }), - ); - - test( - "deferred batch", - withClient(async (c) => { - const rss = await c.batch( - [ - "SELECT 1+1", - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - "INSERT INTO t VALUES (21) RETURNING 2*a", - ], - "deferred", - ); - - expect(rss.length).toStrictEqual(4); - const [rs0, _rs1, _rs2, rs3] = rss; - - expect(rs0.rows.length).toStrictEqual(1); - expect(Array.from(rs0.rows[0])).toStrictEqual([2]); - - expect(rs3.rows.length).toStrictEqual(1); - expect(Array.from(rs3.rows[0])).toStrictEqual([42]); - }), - ); - - (hasHrana3 ? test : test.skip)( - "ROLLBACK statement stops execution of batch", - withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a)"); - - await expect( - c.batch( - [ - "INSERT INTO t VALUES (1), (2), (3)", - "ROLLBACK", - "INSERT INTO t VALUES (4), (5)", - ], - "write", - ), - ).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - }), - ); - - test.only( - "with wait set to true", - withClient(async (c) => { - //await c.execute("DROP TABLE IF EXISTS t"); - //await c.execute("CREATE TABLE t (a)"); - - const startTs = Date.now(); - await c.batch( - ["CREATE TABLE t (a)", "ALTER TABLE t ADD COLUMN another_column"], - { - transactionMode: "read", - wait: true, - }, - ); - - const durationInMs = Date.now() - startTs; - expect(durationInMs).toBeGreaterThanOrEqual(1000); - }), - ); + expect(rss.length).toStrictEqual(4); + const [rs0, rs1, rs2, rs3] = rss; + + expect(rs0.rows.length).toStrictEqual(1); + expect(Array.from(rs0.rows[0])).toStrictEqual([2]); + + expect(rs1.rows.length).toStrictEqual(1); + expect(Array.from(rs1.rows[0])).toStrictEqual([1, 2]); + + expect(rs2.rows.length).toStrictEqual(1); + expect(Array.from(rs2.rows[0])).toStrictEqual(["boomerang"]); + + expect(rs3.rows.length).toStrictEqual(2); + expect(Array.from(rs3.rows[0])).toStrictEqual(["big"]); + expect(Array.from(rs3.rows[1])).toStrictEqual(["ben"]); + }), + ); + + test( + "statements are executed sequentially", + withClient(async (c) => { + const rss = await c.batch( + [ + /* 0 */ "DROP TABLE IF EXISTS t", + /* 1 */ "CREATE TABLE t (a, b)", + /* 2 */ "INSERT INTO t VALUES (1, 'one')", + /* 3 */ "SELECT * FROM t ORDER BY a", + /* 4 */ "INSERT INTO t VALUES (2, 'two')", + /* 5 */ "SELECT * FROM t ORDER BY a", + /* 6 */ "DROP TABLE t", + ], + "write", + ); + + expect(rss.length).toStrictEqual(7); + expect(rss[3].rows).toEqual([{ a: 1, b: "one" }]); + expect(rss[5].rows).toEqual([ + { a: 1, b: "one" }, + { a: 2, b: "two" }, + ]); + }), + ); + + test( + "statements are executed in a transaction", + withClient(async (c) => { + await c.batch( + [ + "DROP TABLE IF EXISTS t1", + "DROP TABLE IF EXISTS t2", + "CREATE TABLE t1 (a)", + "CREATE TABLE t2 (a)", + ], + "write", + ); + + const n = 100; + const promises = []; + for (let i = 0; i < n; ++i) { + const ii = i; + promises.push( + (async () => { + const rss = await c.batch( + [ + { + sql: "INSERT INTO t1 VALUES (?)", + args: [ii], + }, + { + sql: "INSERT INTO t2 VALUES (?)", + args: [ii * 10], + }, + "SELECT SUM(a) FROM t1", + "SELECT SUM(a) FROM t2", + ], + "write", + ); + + const sum1 = rss[2].rows[0][0] as number; + const sum2 = rss[3].rows[0][0] as number; + expect(sum2).toStrictEqual(sum1 * 10); + })(), + ); + } + await Promise.all(promises); + + const rs1 = await c.execute("SELECT SUM(a) FROM t1"); + expect(rs1.rows[0][0]).toStrictEqual((n * (n - 1)) / 2); + const rs2 = await c.execute("SELECT SUM(a) FROM t2"); + expect(rs2.rows[0][0]).toStrictEqual(((n * (n - 1)) / 2) * 10); + }), + 10000, + ); + + test( + "error in batch", + withClient(async (c) => { + await expect( + c.batch(["SELECT 1+1", "SELECT foobar"], "read"), + ).rejects.toBeLibsqlError(); + }), + ); + + test( + "error in batch rolls back transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a)"); + await c.execute("INSERT INTO t VALUES ('one')"); + await expect( + c.batch( + [ + "INSERT INTO t VALUES ('two')", + "SELECT foobar", + "INSERT INTO t VALUES ('three')", + ], + "write", + ), + ).rejects.toBeLibsqlError(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(1); + }), + ); + + test( + "batch with a lot of different statements", + withClient(async (c) => { + const stmts = []; + for (let i = 0; i < 1000; ++i) { + stmts.push(`SELECT ${i}`); + } + const rss = await c.batch(stmts, "read"); + for (let i = 0; i < stmts.length; ++i) { + expect(rss[i].rows[0][0]).toStrictEqual(i); + } + }), + ); + + test( + "batch with a lot of the same statements", + withClient(async (c) => { + const n = 20; + const m = 200; + + const stmts = []; + for (let i = 0; i < n; ++i) { + for (let j = 0; j < m; ++j) { + stmts.push({ sql: `SELECT ?, ${j}`, args: [i] }); + } + } + + const rss = await c.batch(stmts, "read"); + for (let i = 0; i < n; ++i) { + for (let j = 0; j < m; ++j) { + const rs = rss[i * m + j]; + expect(rs.rows[0][0]).toStrictEqual(i); + expect(rs.rows[0][1]).toStrictEqual(j); + } + } + }), + ); + + test( + "deferred batch", + withClient(async (c) => { + const rss = await c.batch( + [ + "SELECT 1+1", + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + "INSERT INTO t VALUES (21) RETURNING 2*a", + ], + "deferred", + ); + + expect(rss.length).toStrictEqual(4); + const [rs0, _rs1, _rs2, rs3] = rss; + + expect(rs0.rows.length).toStrictEqual(1); + expect(Array.from(rs0.rows[0])).toStrictEqual([2]); + + expect(rs3.rows.length).toStrictEqual(1); + expect(Array.from(rs3.rows[0])).toStrictEqual([42]); + }), + ); + + (hasHrana3 ? test : test.skip)( + "ROLLBACK statement stops execution of batch", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a)"); + + await expect( + c.batch( + [ + "INSERT INTO t VALUES (1), (2), (3)", + "ROLLBACK", + "INSERT INTO t VALUES (4), (5)", + ], + "write", + ), + ).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); + + test.only( + "with wait set to true", + withClient(async (c) => { + //await c.execute("DROP TABLE IF EXISTS t"); + //await c.execute("CREATE TABLE t (a)"); + + const startTs = Date.now(); + await c.batch( + [ + "CREATE TABLE t (a)", + "ALTER TABLE t ADD COLUMN another_column", + ], + { + transactionMode: "read", + wait: true, + }, + ); + + const durationInMs = Date.now() - startTs; + expect(durationInMs).toBeGreaterThanOrEqual(1000); + }), + ); }); describe("transaction()", () => { - test( - "query multiple rows", - withClient(async (c) => { - const txn = await c.transaction("read"); - - const rs = await txn.execute( - "VALUES (1, 'one'), (2, 'two'), (3, 'three')", - ); - expect(rs.columns.length).toStrictEqual(2); - expect(rs.columnTypes.length).toStrictEqual(2); - expect(rs.rows.length).toStrictEqual(3); - - expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); - expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); - expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); - - txn.close(); - }), - ); - - test( - "commit()", - withClient(async (c) => { - await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); - - const txn = await c.transaction("write"); - await txn.execute("INSERT INTO t VALUES ('one')"); - await txn.execute("INSERT INTO t VALUES ('two')"); - expect(txn.closed).toStrictEqual(false); - await txn.commit(); - expect(txn.closed).toStrictEqual(true); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(2); - await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( - "TRANSACTION_CLOSED", - ); - }), - ); - - test( - "rollback()", - withClient(async (c) => { - await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); - - const txn = await c.transaction("write"); - await txn.execute("INSERT INTO t VALUES ('one')"); - await txn.execute("INSERT INTO t VALUES ('two')"); - expect(txn.closed).toStrictEqual(false); - await txn.rollback(); - expect(txn.closed).toStrictEqual(true); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( - "TRANSACTION_CLOSED", - ); - }), - ); - - test( - "close()", - withClient(async (c) => { - await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); - - const txn = await c.transaction("write"); - await txn.execute("INSERT INTO t VALUES ('one')"); - expect(txn.closed).toStrictEqual(false); - txn.close(); - expect(txn.closed).toStrictEqual(true); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( - "TRANSACTION_CLOSED", - ); - }), - ); - - test( - "error does not rollback", - withClient(async (c) => { - await c.batch(["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], "write"); - - const txn = await c.transaction("write"); - await expect(txn.execute("SELECT foo")).rejects.toBeLibsqlError(); - await txn.execute("INSERT INTO t VALUES ('one')"); - await expect(txn.execute("SELECT bar")).rejects.toBeLibsqlError(); - await txn.commit(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(1); - }), - ); - - (hasHrana3 ? test : test.skip)( - "ROLLBACK statement stops execution of transaction", - withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a)"); - - const txn = await c.transaction("write"); - const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); - const prom2 = txn.execute("ROLLBACK"); - const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); - - await prom1; - await prom2; - await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - await expect(txn.commit()).rejects.toBeLibsqlError(); - txn.close(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - }), - ); - - (hasHrana3 ? test : test.skip)( - "OR ROLLBACK statement stops execution of transaction", - withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a UNIQUE)"); - - const txn = await c.transaction("write"); - const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); - const prom2 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); - const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); - - await prom1; - await expect(prom2).rejects.toBeLibsqlError(); - await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - await expect(txn.commit()).rejects.toBeLibsqlError(); - txn.close(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - }), - ); - - (hasHrana3 ? test : test.skip)( - "OR ROLLBACK as the first statement stops execution of transaction", - withClient(async (c) => { - await c.execute("DROP TABLE IF EXISTS t"); - await c.execute("CREATE TABLE t (a UNIQUE)"); - await c.execute("INSERT INTO t VALUES (1), (2), (3)"); - - const txn = await c.transaction("write"); - const prom1 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); - const prom2 = txn.execute("INSERT INTO t VALUES (4), (5)"); - - await expect(prom1).rejects.toBeLibsqlError(); - await expect(prom2).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); - await expect(txn.commit()).rejects.toBeLibsqlError(); - txn.close(); - - const rs = await c.execute("SELECT COUNT(*) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(3); - }), - ); - - test( - "commit empty", - withClient(async (c) => { - const txn = await c.transaction("read"); - await txn.commit(); - }), - ); - - test( - "rollback empty", - withClient(async (c) => { - const txn = await c.transaction("read"); - await txn.rollback(); - }), - ); - - describe("batch()", () => { test( - "as the first operation on transaction", - withClient(async (c) => { - const txn = await c.transaction("write"); - - await txn.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a)", - { sql: "INSERT INTO t VALUES (?)", args: [1] }, - { sql: "INSERT INTO t VALUES (?)", args: [2] }, - { sql: "INSERT INTO t VALUES (?)", args: [4] }, - ]); - - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - txn.close(); - }), + "query multiple rows", + withClient(async (c) => { + const txn = await c.transaction("read"); + + const rs = await txn.execute( + "VALUES (1, 'one'), (2, 'two'), (3, 'three')", + ); + expect(rs.columns.length).toStrictEqual(2); + expect(rs.columnTypes.length).toStrictEqual(2); + expect(rs.rows.length).toStrictEqual(3); + + expect(Array.from(rs.rows[0])).toStrictEqual([1, "one"]); + expect(Array.from(rs.rows[1])).toStrictEqual([2, "two"]); + expect(Array.from(rs.rows[2])).toStrictEqual([3, "three"]); + + txn.close(); + }), + ); + + test( + "commit()", + withClient(async (c) => { + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + + const txn = await c.transaction("write"); + await txn.execute("INSERT INTO t VALUES ('one')"); + await txn.execute("INSERT INTO t VALUES ('two')"); + expect(txn.closed).toStrictEqual(false); + await txn.commit(); + expect(txn.closed).toStrictEqual(true); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(2); + await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + }), + ); + + test( + "rollback()", + withClient(async (c) => { + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + + const txn = await c.transaction("write"); + await txn.execute("INSERT INTO t VALUES ('one')"); + await txn.execute("INSERT INTO t VALUES ('two')"); + expect(txn.closed).toStrictEqual(false); + await txn.rollback(); + expect(txn.closed).toStrictEqual(true); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + }), + ); + + test( + "close()", + withClient(async (c) => { + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + + const txn = await c.transaction("write"); + await txn.execute("INSERT INTO t VALUES ('one')"); + expect(txn.closed).toStrictEqual(false); + txn.close(); + expect(txn.closed).toStrictEqual(true); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + await expect(txn.execute("SELECT 1")).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + }), ); test( - "as the second operation on transaction", - withClient(async (c) => { - const txn = await c.transaction("write"); - - await txn.execute("DROP TABLE IF EXISTS t"); - await txn.batch([ - "CREATE TABLE t (a)", - { sql: "INSERT INTO t VALUES (?)", args: [1] }, - { sql: "INSERT INTO t VALUES (?)", args: [2] }, - { sql: "INSERT INTO t VALUES (?)", args: [4] }, - ]); - - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - txn.close(); - }), + "error does not rollback", + withClient(async (c) => { + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + + const txn = await c.transaction("write"); + await expect(txn.execute("SELECT foo")).rejects.toBeLibsqlError(); + await txn.execute("INSERT INTO t VALUES ('one')"); + await expect(txn.execute("SELECT bar")).rejects.toBeLibsqlError(); + await txn.commit(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(1); + }), + ); + + (hasHrana3 ? test : test.skip)( + "ROLLBACK statement stops execution of transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a)"); + + const txn = await c.transaction("write"); + const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); + const prom2 = txn.execute("ROLLBACK"); + const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); + + await prom1; + await prom2; + await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + await expect(txn.commit()).rejects.toBeLibsqlError(); + txn.close(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); + + (hasHrana3 ? test : test.skip)( + "OR ROLLBACK statement stops execution of transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a UNIQUE)"); + + const txn = await c.transaction("write"); + const prom1 = txn.execute("INSERT INTO t VALUES (1), (2), (3)"); + const prom2 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); + const prom3 = txn.execute("INSERT INTO t VALUES (4), (5)"); + + await prom1; + await expect(prom2).rejects.toBeLibsqlError(); + await expect(prom3).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + await expect(txn.commit()).rejects.toBeLibsqlError(); + txn.close(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); + + (hasHrana3 ? test : test.skip)( + "OR ROLLBACK as the first statement stops execution of transaction", + withClient(async (c) => { + await c.execute("DROP TABLE IF EXISTS t"); + await c.execute("CREATE TABLE t (a UNIQUE)"); + await c.execute("INSERT INTO t VALUES (1), (2), (3)"); + + const txn = await c.transaction("write"); + const prom1 = txn.execute("INSERT OR ROLLBACK INTO t VALUES (1)"); + const prom2 = txn.execute("INSERT INTO t VALUES (4), (5)"); + + await expect(prom1).rejects.toBeLibsqlError(); + await expect(prom2).rejects.toBeLibsqlError("TRANSACTION_CLOSED"); + await expect(txn.commit()).rejects.toBeLibsqlError(); + txn.close(); + + const rs = await c.execute("SELECT COUNT(*) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(3); + }), ); test( - "after error, further statements are not executed", - withClient(async (c) => { - const txn = await c.transaction("write"); - - await expect( - txn.batch([ - "DROP TABLE IF EXISTS t", - "CREATE TABLE t (a UNIQUE)", - "INSERT INTO t VALUES (1), (2), (4)", - "INSERT INTO t VALUES (1)", - "INSERT INTO t VALUES (8), (16)", - ]), - ).rejects.toBeLibsqlError(); - - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - - await txn.commit(); - }), + "commit empty", + withClient(async (c) => { + const txn = await c.transaction("read"); + await txn.commit(); + }), ); - }); - (hasHrana2 ? describe : describe.skip)("executeMultiple()", () => { test( - "as the first operation on transaction", - withClient(async (c) => { - const txn = await c.transaction("write"); + "rollback empty", + withClient(async (c) => { + const txn = await c.transaction("read"); + await txn.rollback(); + }), + ); + + describe("batch()", () => { + test( + "as the first operation on transaction", + withClient(async (c) => { + const txn = await c.transaction("write"); + + await txn.batch([ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a)", + { sql: "INSERT INTO t VALUES (?)", args: [1] }, + { sql: "INSERT INTO t VALUES (?)", args: [2] }, + { sql: "INSERT INTO t VALUES (?)", args: [4] }, + ]); + + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); + txn.close(); + }), + ); + + test( + "as the second operation on transaction", + withClient(async (c) => { + const txn = await c.transaction("write"); + + await txn.execute("DROP TABLE IF EXISTS t"); + await txn.batch([ + "CREATE TABLE t (a)", + { sql: "INSERT INTO t VALUES (?)", args: [1] }, + { sql: "INSERT INTO t VALUES (?)", args: [2] }, + { sql: "INSERT INTO t VALUES (?)", args: [4] }, + ]); + + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); + txn.close(); + }), + ); + + test( + "after error, further statements are not executed", + withClient(async (c) => { + const txn = await c.transaction("write"); + + await expect( + txn.batch([ + "DROP TABLE IF EXISTS t", + "CREATE TABLE t (a UNIQUE)", + "INSERT INTO t VALUES (1), (2), (4)", + "INSERT INTO t VALUES (1)", + "INSERT INTO t VALUES (8), (16)", + ]), + ).rejects.toBeLibsqlError(); + + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); + + await txn.commit(); + }), + ); + }); + + (hasHrana2 ? describe : describe.skip)("executeMultiple()", () => { + test( + "as the first operation on transaction", + withClient(async (c) => { + const txn = await c.transaction("write"); - await txn.executeMultiple(` + await txn.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4), (8); `); - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(15); - txn.close(); - }), - ); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(15); + txn.close(); + }), + ); - test( - "as the second operation on transaction", - withClient(async (c) => { - const txn = await c.transaction("write"); - await txn.execute("DROP TABLE IF EXISTS t"); - await txn.executeMultiple(` + test( + "as the second operation on transaction", + withClient(async (c) => { + const txn = await c.transaction("write"); + await txn.execute("DROP TABLE IF EXISTS t"); + await txn.executeMultiple(` CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4), (8); `); - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(15); - txn.close(); - }), - ); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(15); + txn.close(); + }), + ); - test( - "after error, further statements are not executed", - withClient(async (c) => { - const txn = await c.transaction("write"); + test( + "after error, further statements are not executed", + withClient(async (c) => { + const txn = await c.transaction("write"); - await expect( - txn.executeMultiple(` + await expect( + txn.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a UNIQUE); INSERT INTO t VALUES (1), (2), (4); INSERT INTO t VALUES (1); INSERT INTO t VALUES (8), (16); `), - ).rejects.toBeLibsqlError(); + ).rejects.toBeLibsqlError(); - const rs = await txn.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); + const rs = await txn.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); - await txn.commit(); - }), - ); - }); + await txn.commit(); + }), + ); + }); }); (hasHrana2 ? describe : describe.skip)("executeMultiple()", () => { - test( - "multiple statements", - withClient(async (c) => { - await c.executeMultiple(` + test( + "multiple statements", + withClient(async (c) => { + await c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4), (8); `); - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(15); - }), - ); + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(15); + }), + ); - test( - "after an error, statements are not executed", - withClient(async (c) => { - await expect( - c.executeMultiple(` + test( + "after an error, statements are not executed", + withClient(async (c) => { + await expect( + c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (1), (2), (4); INSERT INTO t VALUES (foo()); INSERT INTO t VALUES (8), (16); `), - ).rejects.toBeLibsqlError(); + ).rejects.toBeLibsqlError(); - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(7); - }), - ); + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(7); + }), + ); - test( - "manual transaction control statements", - withClient(async (c) => { - await c.executeMultiple(` + test( + "manual transaction control statements", + withClient(async (c) => { + await c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); BEGIN; @@ -1242,16 +1309,16 @@ describe("transaction()", () => { COMMIT; `); - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(31); - }), - ); + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(31); + }), + ); - test( - "error rolls back a manual transaction", - withClient(async (c) => { - await expect( - c.executeMultiple(` + test( + "error rolls back a manual transaction", + withClient(async (c) => { + await expect( + c.executeMultiple(` DROP TABLE IF EXISTS t; CREATE TABLE t (a); INSERT INTO t VALUES (0); @@ -1261,85 +1328,91 @@ describe("transaction()", () => { INSERT INTO t VALUES (8), (16); COMMIT; `), - ).rejects.toBeLibsqlError(); + ).rejects.toBeLibsqlError(); - const rs = await c.execute("SELECT SUM(a) FROM t"); - expect(rs.rows[0][0]).toStrictEqual(0); - }), - ); + const rs = await c.execute("SELECT SUM(a) FROM t"); + expect(rs.rows[0][0]).toStrictEqual(0); + }), + ); }); (hasNetworkErrors ? describe : describe.skip)("network errors", () => { - const testCases = [ - { title: "WebSocket close", sql: ".close_ws" }, - { title: "TCP close", sql: ".close_tcp" }, - ]; + const testCases = [ + { title: "WebSocket close", sql: ".close_ws" }, + { title: "TCP close", sql: ".close_tcp" }, + ]; - for (const { title, sql } of testCases) { - test( - `${title} in execute()`, - withClient(async (c) => { - await expect(c.execute(sql)).rejects.toBeLibsqlError( - "HRANA_WEBSOCKET_ERROR", + for (const { title, sql } of testCases) { + test( + `${title} in execute()`, + withClient(async (c) => { + await expect(c.execute(sql)).rejects.toBeLibsqlError( + "HRANA_WEBSOCKET_ERROR", + ); + + expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual( + 42, + ); + }), ); - expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); - }), - ); - - test( - `${title} in transaction()`, - withClient(async (c) => { - const txn = await c.transaction("read"); - await expect(txn.execute(sql)).rejects.toBeLibsqlError( - "HRANA_WEBSOCKET_ERROR", - ); - await expect(txn.commit()).rejects.toBeLibsqlError( - "TRANSACTION_CLOSED", + test( + `${title} in transaction()`, + withClient(async (c) => { + const txn = await c.transaction("read"); + await expect(txn.execute(sql)).rejects.toBeLibsqlError( + "HRANA_WEBSOCKET_ERROR", + ); + await expect(txn.commit()).rejects.toBeLibsqlError( + "TRANSACTION_CLOSED", + ); + txn.close(); + + expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual( + 42, + ); + }), ); - txn.close(); - expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); - }), - ); + test( + `${title} in batch()`, + withClient(async (c) => { + await expect( + c.batch(["SELECT 42", sql, "SELECT 24"], "read"), + ).rejects.toBeLibsqlError("HRANA_WEBSOCKET_ERROR"); - test( - `${title} in batch()`, - withClient(async (c) => { - await expect( - c.batch(["SELECT 42", sql, "SELECT 24"], "read"), - ).rejects.toBeLibsqlError("HRANA_WEBSOCKET_ERROR"); - - expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual(42); - }), - ); - } + expect((await c.execute("SELECT 42")).rows[0][0]).toStrictEqual( + 42, + ); + }), + ); + } }); (isHttp ? test : test.skip)("custom fetch", async () => { - let fetchCalledCount = 0; - function customFetch(request: Request): Promise { - fetchCalledCount += 1; - return fetch(request); - } - - const c = createClient({ ...config, fetch: customFetch }); - try { - const rs = await c.execute("SELECT 42"); - expect(rs.rows[0][0]).toStrictEqual(42); - expect(fetchCalledCount).toBeGreaterThan(0); - } finally { - c.close(); - } + let fetchCalledCount = 0; + function customFetch(request: Request): Promise { + fetchCalledCount += 1; + return fetch(request); + } + + const c = createClient({ ...config, fetch: customFetch }); + try { + const rs = await c.execute("SELECT 42"); + expect(rs.rows[0][0]).toStrictEqual(42); + expect(fetchCalledCount).toBeGreaterThan(0); + } finally { + c.close(); + } }); (isFile ? test : test.skip)("raw error codes", async () => { - const c = createClient(config); - try { - await expect(c.execute("NOT A VALID SQL")).rejects.toThrow( - expect.toBeLibsqlError({ code: "SQLITE_ERROR", rawCode: 1 }), - ); - } finally { - c.close(); - } + const c = createClient(config); + try { + await expect(c.execute("NOT A VALID SQL")).rejects.toThrow( + expect.toBeLibsqlError({ code: "SQLITE_ERROR", rawCode: 1 }), + ); + } finally { + c.close(); + } }); diff --git a/packages/libsql-client/src/__tests__/handlers.ts b/packages/libsql-client/src/__tests__/handlers.ts index 51d5ce4e..bcdf2a54 100644 --- a/packages/libsql-client/src/__tests__/handlers.ts +++ b/packages/libsql-client/src/__tests__/handlers.ts @@ -1,16 +1,16 @@ import { http, HttpResponse } from "msw"; export const handlers = [ - // Intercept "GET https://example.com/user" requests... - http.get("http://localhost:8080", ({ request }) => { - //console.log("request: ", request); - // print path - console.log("path: ", request.url); - // ...and respond to them using this JSON response. - //return HttpResponse.json({ - //id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', - //firstName: 'John', - //lastName: 'Maverick', - //}) - }), + // Intercept "GET https://example.com/user" requests... + http.get("http://localhost:8080", ({ request }) => { + //console.log("request: ", request); + // print path + console.log("path: ", request.url); + // ...and respond to them using this JSON response. + //return HttpResponse.json({ + //id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', + //firstName: 'John', + //lastName: 'Maverick', + //}) + }), ]; diff --git a/packages/libsql-client/src/__tests__/mocks.ts b/packages/libsql-client/src/__tests__/mocks.ts index da84961e..2e15dd78 100644 --- a/packages/libsql-client/src/__tests__/mocks.ts +++ b/packages/libsql-client/src/__tests__/mocks.ts @@ -3,5 +3,5 @@ import { handlers } from "./handlers"; export const server = setupServer(...handlers); server.events.on("request:start", ({ request }) => { - console.log("Outgoing:", request.method, request.url); + console.log("Outgoing:", request.method, request.url); }); diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 4e3a8e56..4b8e1fca 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -1,13 +1,22 @@ import * as hrana from "@libsql/hrana-client"; import type { Config, Client } from "@libsql/core/api"; -import type { BatchConfig, InStatement, ResultSet, Transaction, IntMode } from "@libsql/core/api"; +import type { + BatchConfig, + InStatement, + ResultSet, + Transaction, + IntMode, +} from "@libsql/core/api"; import { TransactionMode, LibsqlError } from "@libsql/core/api"; import type { ExpandedConfig } from "@libsql/core/config"; import { expandConfig } from "@libsql/core/config"; import { - HranaTransaction, executeHranaBatch, - stmtToHrana, resultSetFromHrana, mapHranaError, + HranaTransaction, + executeHranaBatch, + stmtToHrana, + resultSetFromHrana, + mapHranaError, } from "./hrana.js"; import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; @@ -31,13 +40,22 @@ export function _createClient(config: ExpandedConfig): Client { } if (config.encryptionKey !== undefined) { - throw new LibsqlError("Encryption key is not supported by the remote client.", "ENCRYPTION_KEY_NOT_SUPPORTED"); + throw new LibsqlError( + "Encryption key is not supported by the remote client.", + "ENCRYPTION_KEY_NOT_SUPPORTED", + ); } if (config.scheme === "http" && config.tls) { - throw new LibsqlError(`A "http:" URL cannot opt into TLS by using ?tls=1`, "URL_INVALID"); + throw new LibsqlError( + `A "http:" URL cannot opt into TLS by using ?tls=1`, + "URL_INVALID", + ); } else if (config.scheme === "https" && !config.tls) { - throw new LibsqlError(`A "https:" URL cannot opt out of TLS by using ?tls=0`, "URL_INVALID"); + throw new LibsqlError( + `A "https:" URL cannot opt out of TLS by using ?tls=0`, + "URL_INVALID", + ); } const url = encodeBaseUrl(config.scheme, config.authority, config.path); @@ -87,8 +105,8 @@ export class HttpClient implements Client { } async batch( - stmts: Array, - mode: TransactionMode | BatchConfig = "deferred" + stmts: Array, + mode: TransactionMode | BatchConfig = "deferred", ): Promise> { try { const hranaStmts = stmts.map(stmtToHrana); @@ -109,21 +127,29 @@ export class HttpClient implements Client { // 2. cursor request // 3. pipeline request to close the stream const batch = stream.batch(false); - const transactionMode = typeof mode === "string" ? mode : mode.transactionMode || "deferred"; - resultsPromise = executeHranaBatch(transactionMode, version, batch, hranaStmts); + const transactionMode = + typeof mode === "string" + ? mode + : mode.transactionMode || "deferred"; + resultsPromise = executeHranaBatch( + transactionMode, + version, + batch, + hranaStmts, + ); } finally { stream.closeGracefully(); } const wait = typeof mode === "string" ? false : mode.wait; if (wait) { - await waitForLastMigrationJobToFinish({ - authToken: this.authToken, - baseUrl: this.url.origin, - }) - console.log('Finished waiting for migration jobs'); + await waitForLastMigrationJobToFinish({ + authToken: this.authToken, + baseUrl: this.url.origin, + }); + console.log("Finished waiting for migration jobs"); } else { - console.log("Not waiting for migration jobs"); + console.log("Not waiting for migration jobs"); } return await resultsPromise; @@ -132,10 +158,16 @@ export class HttpClient implements Client { } } - async transaction(mode: TransactionMode = "write"): Promise { + async transaction( + mode: TransactionMode = "write", + ): Promise { try { const version = await this.#client.getVersion(); - return new HttpTransaction(this.#client.openStream(), mode, version); + return new HttpTransaction( + this.#client.openStream(), + mode, + version, + ); } catch (e) { throw mapHranaError(e); } @@ -160,7 +192,10 @@ export class HttpClient implements Client { } sync(): Promise { - throw new LibsqlError("sync not supported in http mode", "SYNC_NOT_SUPPORTED"); + throw new LibsqlError( + "sync not supported in http mode", + "SYNC_NOT_SUPPORTED", + ); } close(): void { @@ -177,7 +212,11 @@ export class HttpTransaction extends HranaTransaction implements Transaction { #sqlCache: SqlCache; /** @private */ - constructor(stream: hrana.HttpStream, mode: TransactionMode, version: hrana.ProtocolVersion) { + constructor( + stream: hrana.HttpStream, + mode: TransactionMode, + version: hrana.ProtocolVersion, + ) { super(mode, version); this.#stream = stream; this.#sqlCache = new SqlCache(stream, sqlCacheCapacity); diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 1f6b911d..deecf679 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -1,133 +1,141 @@ type MigrationJobType = { - job_id: number; - status: string; -} + job_id: number; + status: string; +}; type ExtendedMigrationJobType = MigrationJobType & { - progress: Array<{ - namespace: string - status: string - error: string | null - }>; + progress: Array<{ + namespace: string; + status: string; + error: string | null; + }>; }; type MigrationResult = { - schema_version: number; - migrations: Array; + schema_version: number; + migrations: Array; }; -const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1 -const SCHEMA_MIGRATION_MAX_RETRIES = 2 +const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1; +const SCHEMA_MIGRATION_MAX_RETRIES = 2; async function sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); } type isMigrationJobFinishedProps = { - authToken: string | undefined - baseUrl: string - jobId: number -} + authToken: string | undefined; + baseUrl: string; + jobId: number; +}; async function isMigrationJobFinished({ - authToken, - baseUrl, - jobId -}:isMigrationJobFinishedProps): Promise { - const url = baseUrl + `/v1/jobs/${jobId}`; - console.log("isMigrationJobFinished url:", url) - const result = await fetch(url, { - method: "GET", - headers: { - Authorization: `Bearer ${authToken}`, - }, - }); - const json = (await result.json()) as ExtendedMigrationJobType; - console.log("json:", json) - const job = json as { status: string }; - if(result.status !== 200) { - throw new Error(`Unexpected status code while fetching job status for migration with id ${jobId}: ${result.status}`); - } + authToken, + baseUrl, + jobId, +}: isMigrationJobFinishedProps): Promise { + const url = baseUrl + `/v1/jobs/${jobId}`; + console.log("isMigrationJobFinished url:", url); + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + const json = (await result.json()) as ExtendedMigrationJobType; + console.log("json:", json); + const job = json as { status: string }; + if (result.status !== 200) { + throw new Error( + `Unexpected status code while fetching job status for migration with id ${jobId}: ${result.status}`, + ); + } - if(job.status == "RunFailure") { - throw new Error("Migration job failed"); - } + if (job.status == "RunFailure") { + throw new Error("Migration job failed"); + } -return job.status == "RunSuccess" + return job.status == "RunSuccess"; } type getLastMigrationJobProps = { - authToken: string | undefined - baseUrl: string -} + authToken: string | undefined; + baseUrl: string; +}; async function getLastMigrationJob({ - authToken, - baseUrl -}:getLastMigrationJobProps): Promise { - const url = baseUrl + "/v1/jobs"; - const result = await fetch(url, { - method: "GET", - headers: { - Authorization: `Bearer ${authToken}`, - }, - }); - if(result.status !== 200) { - throw new Error("Unexpected status code while fetching migration jobs: " + result.status); - } + authToken, + baseUrl, +}: getLastMigrationJobProps): Promise { + const url = baseUrl + "/v1/jobs"; + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + if (result.status !== 200) { + throw new Error( + "Unexpected status code while fetching migration jobs: " + + result.status, + ); + } - const json = (await result.json()) as MigrationResult; - console.log("json:", json) - if(!json.migrations || json.migrations.length === 0) { - throw new Error("No migrations found"); - } + const json = (await result.json()) as MigrationResult; + console.log("json:", json); + if (!json.migrations || json.migrations.length === 0) { + throw new Error("No migrations found"); + } - const migrations = json.migrations || []; - let lastJob: MigrationJobType | undefined; - for (const migration of migrations) { - if (migration.job_id > (lastJob?.job_id || 0)) { - lastJob = migration; + const migrations = json.migrations || []; + let lastJob: MigrationJobType | undefined; + for (const migration of migrations) { + if (migration.job_id > (lastJob?.job_id || 0)) { + lastJob = migration; + } + } + if (!lastJob) { + throw new Error("No migration job found"); + } + if (lastJob?.status === "RunFailure") { + throw new Error("Last migration job failed"); } - } - if(!lastJob) { - throw new Error("No migration job found"); - } - if (lastJob?.status === "RunFailure") { - throw new Error("Last migration job failed"); - } - return lastJob; + return lastJob; } type waitForLastMigrationJobToFinishProps = { - authToken: string | undefined - baseUrl: string -} + authToken: string | undefined; + baseUrl: string; +}; export async function waitForLastMigrationJobToFinish({ - authToken, - baseUrl -}:getLastMigrationJobProps) { - console.log('Waiting for migration jobs'); - const lastMigrationJob = await getLastMigrationJob({ - authToken: authToken, - baseUrl - }); - console.log("lastMigrationJob:", lastMigrationJob) - if(lastMigrationJob.status !== "RunSuccess") { - let i = 0 - while(i < SCHEMA_MIGRATION_MAX_RETRIES) { - i++; - console.log("Waiting for migration job to finish, attempt:", i); - const isLastMigrationJobFinished = await isMigrationJobFinished({ + authToken, + baseUrl, +}: getLastMigrationJobProps) { + console.log("Waiting for migration jobs"); + const lastMigrationJob = await getLastMigrationJob({ authToken: authToken, baseUrl, - jobId: lastMigrationJob.job_id - }) - console.log("isLastMigrationJobFinished:", isLastMigrationJobFinished) - await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); + }); + console.log("lastMigrationJob:", lastMigrationJob); + if (lastMigrationJob.status !== "RunSuccess") { + let i = 0; + while (i < SCHEMA_MIGRATION_MAX_RETRIES) { + i++; + console.log("Waiting for migration job to finish, attempt:", i); + const isLastMigrationJobFinished = await isMigrationJobFinished({ + authToken: authToken, + baseUrl, + jobId: lastMigrationJob.job_id, + }); + console.log( + "isLastMigrationJobFinished:", + isLastMigrationJobFinished, + ); + await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); + } } - } } diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index a785aa4c..5aabd1ce 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -1,12 +1,22 @@ import * as hrana from "@libsql/hrana-client"; -import type { Config, IntMode, Client, Transaction, ResultSet, InStatement } from "@libsql/core/api"; +import type { + Config, + IntMode, + Client, + Transaction, + ResultSet, + InStatement, +} from "@libsql/core/api"; import { TransactionMode, BatchConfig, LibsqlError } from "@libsql/core/api"; import type { ExpandedConfig } from "@libsql/core/config"; import { expandConfig } from "@libsql/core/config"; import { - HranaTransaction, executeHranaBatch, - stmtToHrana, resultSetFromHrana, mapHranaError, + HranaTransaction, + executeHranaBatch, + stmtToHrana, + resultSetFromHrana, + mapHranaError, } from "./hrana.js"; import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; @@ -29,13 +39,22 @@ export function _createClient(config: ExpandedConfig): WsClient { } if (config.encryptionKey !== undefined) { - throw new LibsqlError("Encryption key is not supported by the remote client.", "ENCRYPTION_KEY_NOT_SUPPORTED"); + throw new LibsqlError( + "Encryption key is not supported by the remote client.", + "ENCRYPTION_KEY_NOT_SUPPORTED", + ); } if (config.scheme === "ws" && config.tls) { - throw new LibsqlError(`A "ws:" URL cannot opt into TLS by using ?tls=1`, "URL_INVALID"); + throw new LibsqlError( + `A "ws:" URL cannot opt into TLS by using ?tls=1`, + "URL_INVALID", + ); } else if (config.scheme === "wss" && !config.tls) { - throw new LibsqlError(`A "wss:" URL cannot opt out of TLS by using ?tls=0`, "URL_INVALID"); + throw new LibsqlError( + `A "wss:" URL cannot opt out of TLS by using ?tls=0`, + "URL_INVALID", + ); } const url = encodeBaseUrl(config.scheme, config.authority, config.path); @@ -46,7 +65,11 @@ export function _createClient(config: ExpandedConfig): WsClient { } catch (e) { if (e instanceof hrana.WebSocketUnsupportedError) { const suggestedScheme = config.scheme === "wss" ? "https" : "http"; - const suggestedUrl = encodeBaseUrl(suggestedScheme, config.authority, config.path); + const suggestedUrl = encodeBaseUrl( + suggestedScheme, + config.authority, + config.path, + ); throw new LibsqlError( "This environment does not support WebSockets, please switch to the HTTP client by using " + `a "${suggestedScheme}:" URL (${JSON.stringify(suggestedUrl)}). ` + @@ -70,7 +93,7 @@ interface ConnState { useSqlCache: boolean | undefined; // The cache of SQL texts stored on the server. Initially has capacity 0, but it is set to // `sqlCacheCapacity` when `useSqlCache` is set to `true`. - sqlCache: SqlCache, + sqlCache: SqlCache; // The time when the connection was opened. openTime: Date; // Set of all `StreamState`-s that were opened from this connection. We can safely close the connection @@ -83,7 +106,7 @@ interface StreamState { stream: hrana.WsStream; } -const maxConnAgeMillis = 60*1000; +const maxConnAgeMillis = 60 * 1000; const sqlCacheCapacity = 100; export class WsClient implements Client { @@ -99,7 +122,12 @@ export class WsClient implements Client { protocol: "ws"; /** @private */ - constructor(client: hrana.WsClient, url: URL, authToken: string | undefined, intMode: IntMode) { + constructor( + client: hrana.WsClient, + url: URL, + authToken: string | undefined, + intMode: IntMode, + ) { this.#url = url; this.#authToken = authToken; this.#intMode = intMode; @@ -128,7 +156,10 @@ export class WsClient implements Client { } } - async batch(stmts: Array, mode: TransactionMode | BatchConfig = "deferred"): Promise> { + async batch( + stmts: Array, + mode: TransactionMode | BatchConfig = "deferred", + ): Promise> { const streamState = await this.#openStream(); try { const hranaStmts = stmts.map(stmtToHrana); @@ -138,8 +169,15 @@ export class WsClient implements Client { // network roundtrip. streamState.conn.sqlCache.apply(hranaStmts); const batch = streamState.stream.batch(version >= 3); - const transactionMode = (typeof mode === "string" ? mode : mode.transactionMode) || "deferred"; - const resultsPromise = executeHranaBatch(transactionMode, version, batch, hranaStmts); + const transactionMode = + (typeof mode === "string" ? mode : mode.transactionMode) || + "deferred"; + const resultsPromise = executeHranaBatch( + transactionMode, + version, + batch, + hranaStmts, + ); return await resultsPromise; } catch (e) { throw mapHranaError(e); @@ -189,7 +227,10 @@ export class WsClient implements Client { const now = new Date(); const ageMillis = now.valueOf() - this.#connState.openTime.valueOf(); - if (ageMillis > maxConnAgeMillis && this.#futureConnState === undefined) { + if ( + ageMillis > maxConnAgeMillis && + this.#futureConnState === undefined + ) { // The existing connection is too old, let's open a new one. const futureConnState = this.#openConn(); this.#futureConnState = futureConnState; @@ -247,7 +288,8 @@ export class WsClient implements Client { // this does not increase latency, because any messages that we would send on the WebSocket before // the handshake would be queued until the handshake is completed anyway. if (connState.useSqlCache === undefined) { - connState.useSqlCache = await connState.client.getVersion() >= 2; + connState.useSqlCache = + (await connState.client.getVersion()) >= 2; if (connState.useSqlCache) { connState.sqlCache.capacity = sqlCacheCapacity; } @@ -255,7 +297,7 @@ export class WsClient implements Client { const stream = connState.client.openStream(); stream.intMode = this.#intMode; - const streamState = {conn: connState, stream}; + const streamState = { conn: connState, stream }; connState.streamStates.add(streamState); return streamState; } catch (e) { @@ -283,7 +325,10 @@ export class WsClient implements Client { const connState = streamState.conn; connState.streamStates.delete(streamState); - if (connState.streamStates.size === 0 && connState !== this.#connState) { + if ( + connState.streamStates.size === 0 && + connState !== this.#connState + ) { // We are not using this connection anymore and this is the last stream that was using it, so we // must close it now. connState.client.close(); @@ -301,7 +346,12 @@ export class WsTransaction extends HranaTransaction implements Transaction { #streamState: StreamState; /** @private */ - constructor(client: WsClient, state: StreamState, mode: TransactionMode, version: hrana.ProtocolVersion) { + constructor( + client: WsClient, + state: StreamState, + mode: TransactionMode, + version: hrana.ProtocolVersion, + ) { super(mode, version); this.#client = client; this.#streamState = state; diff --git a/packages/libsql-core/src/api.ts b/packages/libsql-core/src/api.ts index 37b14d12..22ee7543 100644 --- a/packages/libsql-core/src/api.ts +++ b/packages/libsql-core/src/api.ts @@ -88,7 +88,7 @@ export interface Client { * * The `mode` parameter selects the transaction mode for the batch; please see {@link TransactionMode} for * details. The default transaction mode is `"deferred"`. - * + * * If any of the statements in the batch fails with an error, the batch is aborted, the transaction is * rolled back and the returned promise is rejected. * @@ -114,7 +114,10 @@ export interface Client { * ], "write"); * ``` */ - batch(stmts: Array, mode?: TransactionMode | BatchConfig): Promise>; + batch( + stmts: Array, + mode?: TransactionMode | BatchConfig, + ): Promise>; /** Start an interactive transaction. * @@ -336,9 +339,9 @@ export interface Transaction { */ export type TransactionMode = "write" | "read" | "deferred"; export type BatchConfig = { - transactionMode?: TransactionMode; - wait?: boolean; -} + transactionMode?: TransactionMode; + wait?: boolean; +}; /** Result of executing an SQL statement. * @@ -371,7 +374,7 @@ export interface ResultSet { * returned. */ columnTypes: Array; - + /** Rows produced by the statement. */ rows: Array; @@ -423,20 +426,11 @@ export interface Row { [name: string]: Value; } -export type Value = - | null - | string - | number - | bigint - | ArrayBuffer +export type Value = null | string | number | bigint | ArrayBuffer; -export type InValue = - | Value - | boolean - | Uint8Array - | Date +export type InValue = Value | boolean | Uint8Array | Date; -export type InStatement = { sql: string, args: InArgs } | string; +export type InStatement = { sql: string; args: InArgs } | string; export type InArgs = Array | Record; /** Error thrown by the client. */ @@ -445,14 +439,19 @@ export class LibsqlError extends Error { code: string; /** Raw numeric error code */ rawCode?: number; - - constructor(message: string, code: string, rawCode?: number, cause?: Error) { + + constructor( + message: string, + code: string, + rawCode?: number, + cause?: Error, + ) { if (code !== undefined) { message = `${code}: ${message}`; } super(message, { cause }); this.code = code; - this.rawCode = rawCode + this.rawCode = rawCode; this.name = "LibsqlError"; } } diff --git a/test.ts b/test.ts index e4e1975f..9d515485 100644 --- a/test.ts +++ b/test.ts @@ -8,36 +8,39 @@ const createClient = libsqlClient.createClient; //}); const schemaUrl = "libsql://schema-test-giovannibenussi.turso.io"; const schemaAuthToken = - "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODIwMTAsImlkIjoiM2IyYTIwMDEtOTcxZC00MzIzLWE2YWYtMjk1YTRmOWNkYzVkIn0.l-LzYur2KffpkrZog5vT3eThwB3m2Nl0RIgc5rLn1DpBsYyWujPTkpS62WoYBwWbM0AMaAoRqfyCzi-T-LnJBQ"; + "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODIwMTAsImlkIjoiM2IyYTIwMDEtOTcxZC00MzIzLWE2YWYtMjk1YTRmOWNkYzVkIn0.l-LzYur2KffpkrZog5vT3eThwB3m2Nl0RIgc5rLn1DpBsYyWujPTkpS62WoYBwWbM0AMaAoRqfyCzi-T-LnJBQ"; const client = createClient({ - url: "libsql://schema-child-1-giovannibenussi.turso.io", - authToken: - "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODE0MDIsImlkIjoiYjJhYjRhNjQtNDAyYy00YmRmLWExZTgtMjdlZjMzNTE4Y2JkIn0.Og9E7nl_Y8P93FO1XJlvAhkKEOsGynDdFEziJwLeGrMNaAOhQLqdxk7shao13VQo4JVFkMuSTXMibKXuPnavBA", + url: "libsql://schema-child-1-giovannibenussi.turso.io", + authToken: + "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODE0MDIsImlkIjoiYjJhYjRhNjQtNDAyYy00YmRmLWExZTgtMjdlZjMzNTE4Y2JkIn0.Og9E7nl_Y8P93FO1XJlvAhkKEOsGynDdFEziJwLeGrMNaAOhQLqdxk7shao13VQo4JVFkMuSTXMibKXuPnavBA", }); const schemaClient = createClient({ - url: schemaUrl, - authToken: schemaAuthToken, + url: schemaUrl, + authToken: schemaAuthToken, }); async function main() { - //await schemaClient.execute("ALTER TABLE users ADD COLUMN test_column_5 number"); - await schemaClient.batch(["ALTER TABLE users ADD COLUMN test_column_6 number;"], { - wait: true, - }); + //await schemaClient.execute("ALTER TABLE users ADD COLUMN test_column_5 number"); + await schemaClient.batch( + ["ALTER TABLE users ADD COLUMN test_column_6 number;"], + { + wait: true, + }, + ); - //await client.batch( + //await client.batch( //[ - //{ - //sql: "insert into users (first_name) values (?)", - //args: [`Iku ${new Date().toISOString()}`], - //}, + //{ + //sql: "insert into users (first_name) values (?)", + //args: [`Iku ${new Date().toISOString()}`], + //}, //], //{ transactionMode: "write", wait: true } - //); - //const users = await client.execute("SELECT * FROM users"); - //console.log("users: ", users.rows); + //); + //const users = await client.execute("SELECT * FROM users"); + //console.log("users: ", users.rows); } main(); From d018b49f665f048badca0ee6b11c4da14b0f901c Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 15:16:53 -0400 Subject: [PATCH 11/45] Add wait option to execute --- packages/libsql-client/src/http.ts | 18 +++++++++++++++++- test.ts | 21 +++------------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 4b8e1fca..7a6048b6 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -84,7 +84,11 @@ export class HttpClient implements Client { this.authToken = authToken; } - async execute(stmt: InStatement): Promise { + async execute( + stmt: InStatement, + mode: BatchConfig = {}, + ): Promise { + console.log("mode: ", mode); try { const hranaStmt = stmtToHrana(stmt); @@ -98,6 +102,13 @@ export class HttpClient implements Client { stream.closeGracefully(); } + if (mode.wait) { + await waitForLastMigrationJobToFinish({ + authToken: this.authToken, + baseUrl: this.url.origin, + }); + } + return resultSetFromHrana(await rowsPromise); } catch (e) { throw mapHranaError(e); @@ -108,8 +119,13 @@ export class HttpClient implements Client { stmts: Array, mode: TransactionMode | BatchConfig = "deferred", ): Promise> { + console.log("stmts: ", stmts); try { const hranaStmts = stmts.map(stmtToHrana); + console.log( + "hranaStmts.map((s)=>s.sql): ", + hranaStmts.map((s) => s.sql), + ); const version = await this.#client.getVersion(); // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and diff --git a/test.ts b/test.ts index 9d515485..8d09bd2e 100644 --- a/test.ts +++ b/test.ts @@ -22,25 +22,10 @@ const schemaClient = createClient({ }); async function main() { - //await schemaClient.execute("ALTER TABLE users ADD COLUMN test_column_5 number"); - await schemaClient.batch( - ["ALTER TABLE users ADD COLUMN test_column_6 number;"], - { - wait: true, - }, + await schemaClient.execute( + "ALTER TABLE users ADD COLUMN test_column_8 number;", + { wait: true }, ); - - //await client.batch( - //[ - //{ - //sql: "insert into users (first_name) values (?)", - //args: [`Iku ${new Date().toISOString()}`], - //}, - //], - //{ transactionMode: "write", wait: true } - //); - //const users = await client.execute("SELECT * FROM users"); - //console.log("users: ", users.rows); } main(); From e93771914dc1c2647f8249d6d40f31f3ab00d597 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 15:17:51 -0400 Subject: [PATCH 12/45] Remove wait option from batch --- packages/libsql-client/src/http.ts | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 7a6048b6..7e4e1985 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -117,15 +117,10 @@ export class HttpClient implements Client { async batch( stmts: Array, - mode: TransactionMode | BatchConfig = "deferred", + mode: TransactionMode = "deferred", ): Promise> { - console.log("stmts: ", stmts); try { const hranaStmts = stmts.map(stmtToHrana); - console.log( - "hranaStmts.map((s)=>s.sql): ", - hranaStmts.map((s) => s.sql), - ); const version = await this.#client.getVersion(); // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and @@ -143,12 +138,8 @@ export class HttpClient implements Client { // 2. cursor request // 3. pipeline request to close the stream const batch = stream.batch(false); - const transactionMode = - typeof mode === "string" - ? mode - : mode.transactionMode || "deferred"; resultsPromise = executeHranaBatch( - transactionMode, + mode, version, batch, hranaStmts, @@ -157,17 +148,6 @@ export class HttpClient implements Client { stream.closeGracefully(); } - const wait = typeof mode === "string" ? false : mode.wait; - if (wait) { - await waitForLastMigrationJobToFinish({ - authToken: this.authToken, - baseUrl: this.url.origin, - }); - console.log("Finished waiting for migration jobs"); - } else { - console.log("Not waiting for migration jobs"); - } - return await resultsPromise; } catch (e) { throw mapHranaError(e); From 95d9d690086d3a73f89b5764e6a5e72547aea769 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 15:19:36 -0400 Subject: [PATCH 13/45] Remove wait option from batch on the ws module --- packages/libsql-client/src/ws.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 5aabd1ce..8b5f0c00 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -8,7 +8,7 @@ import type { ResultSet, InStatement, } from "@libsql/core/api"; -import { TransactionMode, BatchConfig, LibsqlError } from "@libsql/core/api"; +import { TransactionMode, LibsqlError } from "@libsql/core/api"; import type { ExpandedConfig } from "@libsql/core/config"; import { expandConfig } from "@libsql/core/config"; import { @@ -158,7 +158,7 @@ export class WsClient implements Client { async batch( stmts: Array, - mode: TransactionMode | BatchConfig = "deferred", + mode: TransactionMode = "deferred", ): Promise> { const streamState = await this.#openStream(); try { @@ -169,15 +169,13 @@ export class WsClient implements Client { // network roundtrip. streamState.conn.sqlCache.apply(hranaStmts); const batch = streamState.stream.batch(version >= 3); - const transactionMode = - (typeof mode === "string" ? mode : mode.transactionMode) || - "deferred"; const resultsPromise = executeHranaBatch( - transactionMode, + mode, version, batch, hranaStmts, ); + return await resultsPromise; } catch (e) { throw mapHranaError(e); From 0868c2594bdb16e7148e5685ed3934d318e7761b Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 15:22:48 -0400 Subject: [PATCH 14/45] Remove unused test --- .../src/__tests__/client.test.ts | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index b1aa3fe8..1188f644 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -930,29 +930,6 @@ describe("batch()", () => { expect(rs.rows[0][0]).toStrictEqual(0); }), ); - - test.only( - "with wait set to true", - withClient(async (c) => { - //await c.execute("DROP TABLE IF EXISTS t"); - //await c.execute("CREATE TABLE t (a)"); - - const startTs = Date.now(); - await c.batch( - [ - "CREATE TABLE t (a)", - "ALTER TABLE t ADD COLUMN another_column", - ], - { - transactionMode: "read", - wait: true, - }, - ); - - const durationInMs = Date.now() - startTs; - expect(durationInMs).toBeGreaterThanOrEqual(1000); - }), - ); }); describe("transaction()", () => { From 4347bae49bcd3c4bc8303acb4b56c20d3fa1d9cb Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 15:24:00 -0400 Subject: [PATCH 15/45] Remove unused test dependencies --- package-lock.json | 295 ------------------ packages/libsql-client/package.json | 1 - .../src/__tests__/client.test.ts | 5 - .../libsql-client/src/__tests__/handlers.ts | 16 - packages/libsql-client/src/__tests__/mocks.ts | 7 - 5 files changed, 324 deletions(-) delete mode 100644 packages/libsql-client/src/__tests__/handlers.ts delete mode 100644 packages/libsql-client/src/__tests__/mocks.ts diff --git a/package-lock.json b/package-lock.json index ae9dbc2d..a76dbbbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,105 +597,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@bundled-es-modules/cookie": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "cookie": "^0.5.0" - } - }, - "node_modules/@bundled-es-modules/statuses": { - "version": "1.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "statuses": "^2.0.1" - } - }, - "node_modules/@inquirer/confirm": { - "version": "3.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^8.2.0", - "@inquirer/type": "^1.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core": { - "version": "8.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.1", - "@inquirer/type": "^1.3.1", - "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.11", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core/node_modules/@types/node": { - "version": "20.12.12", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@inquirer/core/node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type": { - "version": "1.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -1078,53 +979,10 @@ "sqlite-wasm": "bin/index.js" } }, - "node_modules/@mswjs/cookies": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@mswjs/interceptors": { - "version": "0.29.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.2.1", - "strict-event-emitter": "^0.5.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@neon-rs/load": { "version": "0.0.4", "license": "MIT" }, - "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "dev": true, @@ -1183,11 +1041,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "dev": true, - "license": "MIT" - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "dev": true, @@ -1226,14 +1079,6 @@ "pretty-format": "^29.0.0" } }, - "node_modules/@types/mute-stream": { - "version": "0.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { "version": "18.19.8", "license": "MIT", @@ -1246,16 +1091,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/statuses": { - "version": "2.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/wrap-ansi": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ws": { "version": "8.5.10", "license": "MIT", @@ -1609,17 +1444,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -1681,14 +1505,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/cli-width": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, "node_modules/cliui": { "version": "8.0.1", "dev": true, @@ -1755,14 +1571,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookie": { - "version": "0.5.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/create-jest": { "version": "29.7.0", "dev": true, @@ -2125,14 +1933,6 @@ "dev": true, "license": "ISC" }, - "node_modules/graphql": { - "version": "16.8.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -2152,11 +1952,6 @@ "node": ">= 0.4" } }, - "node_modules/headers-polyfill": { - "version": "4.0.3", - "dev": true, - "license": "MIT" - }, "node_modules/html-escaper": { "version": "2.0.2", "dev": true, @@ -2256,11 +2051,6 @@ "node": ">=6" } }, - "node_modules/is-node-process": { - "version": "1.2.0", - "dev": true, - "license": "MIT" - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3571,67 +3361,6 @@ "version": "2.1.2", "license": "MIT" }, - "node_modules/msw": { - "version": "2.3.0", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@bundled-es-modules/cookie": "^2.0.0", - "@bundled-es-modules/statuses": "^1.0.1", - "@inquirer/confirm": "^3.0.0", - "@mswjs/cookies": "^1.1.0", - "@mswjs/interceptors": "^0.29.0", - "@open-draft/until": "^2.1.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "chalk": "^4.1.2", - "graphql": "^16.8.1", - "headers-polyfill": "^4.0.2", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.2", - "path-to-regexp": "^6.2.0", - "strict-event-emitter": "^0.5.1", - "type-fest": "^4.9.0", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.7.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/msw/node_modules/type-fest": { - "version": "4.18.2", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mute-stream": { - "version": "1.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "dev": true, @@ -3720,11 +3449,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/outvariant": { - "version": "1.4.2", - "dev": true, - "license": "MIT" - }, "node_modules/p-limit": { "version": "3.1.0", "dev": true, @@ -3817,11 +3541,6 @@ "dev": true, "license": "MIT" }, - "node_modules/path-to-regexp": { - "version": "6.2.2", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.0.0", "dev": true, @@ -4133,19 +3852,6 @@ "node": ">=10" } }, - "node_modules/statuses": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/strict-event-emitter": { - "version": "0.5.1", - "dev": true, - "license": "MIT" - }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -4633,7 +4339,6 @@ "husky": "^9.0.11", "jest": "^29.3.1", "lint-staged": "^15.2.2", - "msw": "^2.3.0", "prettier": "3.2.5", "ts-jest": "^29.0.5", "typedoc": "^0.23.28", diff --git a/packages/libsql-client/package.json b/packages/libsql-client/package.json index b0ec8660..259a9a3f 100644 --- a/packages/libsql-client/package.json +++ b/packages/libsql-client/package.json @@ -114,7 +114,6 @@ "jest": "^29.3.1", "lint-staged": "^15.2.2", "prettier": "3.2.5", - "msw": "^2.3.0", "ts-jest": "^29.0.5", "typedoc": "^0.23.28", "typescript": "^4.9.4" diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 1188f644..7d230e96 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -9,11 +9,6 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; -import { server as mswServer } from "./mocks"; - -beforeAll(() => mswServer.listen()); -afterEach(() => mswServer.resetHandlers()); -afterAll(() => mswServer.close()); const config = { url: process.env.URL ?? "ws://localhost:8080", diff --git a/packages/libsql-client/src/__tests__/handlers.ts b/packages/libsql-client/src/__tests__/handlers.ts deleted file mode 100644 index bcdf2a54..00000000 --- a/packages/libsql-client/src/__tests__/handlers.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { http, HttpResponse } from "msw"; - -export const handlers = [ - // Intercept "GET https://example.com/user" requests... - http.get("http://localhost:8080", ({ request }) => { - //console.log("request: ", request); - // print path - console.log("path: ", request.url); - // ...and respond to them using this JSON response. - //return HttpResponse.json({ - //id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3d', - //firstName: 'John', - //lastName: 'Maverick', - //}) - }), -]; diff --git a/packages/libsql-client/src/__tests__/mocks.ts b/packages/libsql-client/src/__tests__/mocks.ts deleted file mode 100644 index 2e15dd78..00000000 --- a/packages/libsql-client/src/__tests__/mocks.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { setupServer } from "msw/node"; -import { handlers } from "./handlers"; - -export const server = setupServer(...handlers); -server.events.on("request:start", ({ request }) => { - console.log("Outgoing:", request.method, request.url); -}); From 7eec7b8057d1efacf15d56a644cd1842311e7327 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Fri, 24 May 2024 15:30:48 -0400 Subject: [PATCH 16/45] Set time between retries to 5 seconds and maximum retries to 6 --- packages/libsql-client/src/migrations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index deecf679..2f6cb10b 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -16,8 +16,8 @@ type MigrationResult = { migrations: Array; }; -const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1; -const SCHEMA_MIGRATION_MAX_RETRIES = 2; +const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 5000; +const SCHEMA_MIGRATION_MAX_RETRIES = 6; async function sleep(ms: number) { return new Promise((resolve) => { From 3c4132709fb7e2346a76e7777610edc30f2f737a Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 27 May 2024 03:44:09 -0400 Subject: [PATCH 17/45] Add tests for waitForLastMigrationJobToFinish --- .../src/__tests__/client.test.ts | 34 +++++++++++++++++++ .../src/__tests__/migrations.test.ts | 15 ++++++++ .../src/__tests__/mocks/handlers.ts | 34 +++++++++++++++++++ .../libsql-client/src/__tests__/mocks/node.ts | 4 +++ packages/libsql-client/src/http.ts | 12 +++---- packages/libsql-client/src/migrations.ts | 9 ++++- packages/libsql-client/src/ws.ts | 16 ++++++++- packages/libsql-core/src/api.ts | 2 +- test.ts | 2 +- 9 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 packages/libsql-client/src/__tests__/migrations.test.ts create mode 100644 packages/libsql-client/src/__tests__/mocks/handlers.ts create mode 100644 packages/libsql-client/src/__tests__/mocks/node.ts diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 7d230e96..4bb0895b 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -9,6 +9,21 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; +import { waitForLastMigrationJobToFinish } from "../migrations"; +import { server as mswServer } from "./mocks/node"; + +beforeAll(() => mswServer.listen()); +afterEach(() => mswServer.resetHandlers()); +afterAll(() => mswServer.close()); + +jest.mock("../migrations", () => { + const originalModule = jest.requireActual("../migrations"); + + return { + ...originalModule, + waitForLastMigrationJobToFinish: jest.fn(), + }; +}); const config = { url: process.env.URL ?? "ws://localhost:8080", @@ -286,6 +301,25 @@ describe("execute()", () => { expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); }), ); + + test( + "calls waitForLastMigrationJobToFinish when the wait parameter is set to true", + withClient(async (c) => { + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + await c.execute("ALTER TABLE t ADD COLUMN another_column number", { + wait: false, + }); + expect(waitForLastMigrationJobToFinish).not.toHaveBeenCalled(); + + await c.execute("ALTER TABLE t ADD COLUMN test_column number", { + wait: true, + }); + expect(waitForLastMigrationJobToFinish).toHaveBeenCalled(); + }), + ); }); describe("values", () => { diff --git a/packages/libsql-client/src/__tests__/migrations.test.ts b/packages/libsql-client/src/__tests__/migrations.test.ts new file mode 100644 index 00000000..52c16370 --- /dev/null +++ b/packages/libsql-client/src/__tests__/migrations.test.ts @@ -0,0 +1,15 @@ +import { waitForLastMigrationJobToFinish } from "../migrations"; +import { server } from "./mocks/node"; + +beforeAll(() => server.listen()); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +describe("waitForLastMigrationJobToFinish()", () => { + test("waits until the last job is completed", async () => { + await waitForLastMigrationJobToFinish({ + authToken: "fake-auth-token", + baseUrl: "http://fake-base-url.example.com", + }); + }); +}); diff --git a/packages/libsql-client/src/__tests__/mocks/handlers.ts b/packages/libsql-client/src/__tests__/mocks/handlers.ts new file mode 100644 index 00000000..0012e6de --- /dev/null +++ b/packages/libsql-client/src/__tests__/mocks/handlers.ts @@ -0,0 +1,34 @@ +import { http, HttpResponse } from "msw"; + +export const handlers = [ + http.get("http://fake-base-url.example.com/v1/jobs", () => { + return HttpResponse.json({ + schema_version: 4, + migrations: [ + { job_id: 4, status: "WaitingDryRun" }, + { job_id: 3, status: "RunSuccess" }, + { job_id: 2, status: "RunSuccess" }, + { job_id: 1, status: "RunSuccess" }, + ], + }); + }), + + http.get( + "http://fake-base-url.example.com/v1/jobs/:job_id", + ({ params }) => { + const { job_id } = params; + + return HttpResponse.json({ + job_id, + status: "RunSuccess", + progress: [ + { + namespace: "b2ab4a64-402c-4bdf-a1e8-27ef33518cbd", + status: "RunSuccess", + error: null, + }, + ], + }); + }, + ), +]; diff --git a/packages/libsql-client/src/__tests__/mocks/node.ts b/packages/libsql-client/src/__tests__/mocks/node.ts new file mode 100644 index 00000000..7b37f2ac --- /dev/null +++ b/packages/libsql-client/src/__tests__/mocks/node.ts @@ -0,0 +1,4 @@ +import { setupServer } from "msw/node"; +import { handlers } from "./handlers"; + +export const server = setupServer(...handlers); diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 7e4e1985..26e94278 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -67,8 +67,8 @@ const sqlCacheCapacity = 30; export class HttpClient implements Client { #client: hrana.HttpClient; protocol: "http"; - url: URL; - authToken: string | undefined; + #url: URL; + #authToken: string | undefined; /** @private */ constructor( @@ -80,8 +80,8 @@ export class HttpClient implements Client { this.#client = hrana.openHttp(url, authToken, customFetch); this.#client.intMode = intMode; this.protocol = "http"; - this.url = url; - this.authToken = authToken; + this.#url = url; + this.#authToken = authToken; } async execute( @@ -104,8 +104,8 @@ export class HttpClient implements Client { if (mode.wait) { await waitForLastMigrationJobToFinish({ - authToken: this.authToken, - baseUrl: this.url.origin, + authToken: this.#authToken, + baseUrl: this.#url.origin, }); } diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 2f6cb10b..7161a9b3 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -125,7 +125,10 @@ export async function waitForLastMigrationJobToFinish({ let i = 0; while (i < SCHEMA_MIGRATION_MAX_RETRIES) { i++; - console.log("Waiting for migration job to finish, attempt:", i); + console.log( + `${i}: Waiting for migration job to finish, attempt:`, + i, + ); const isLastMigrationJobFinished = await isMigrationJobFinished({ authToken: authToken, baseUrl, @@ -135,6 +138,10 @@ export async function waitForLastMigrationJobToFinish({ "isLastMigrationJobFinished:", isLastMigrationJobFinished, ); + if (isLastMigrationJobFinished) { + break; + } + await sleep(SCHEMA_MIGRATION_SLEEP_TIME_IN_MS); } } diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 8b5f0c00..b118d5f3 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -1,6 +1,7 @@ import * as hrana from "@libsql/hrana-client"; import type { + BatchConfig, Config, IntMode, Client, @@ -21,6 +22,7 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; +import { waitForLastMigrationJobToFinish } from "./migrations"; export * from "@libsql/core/api"; @@ -137,7 +139,11 @@ export class WsClient implements Client { this.protocol = "ws"; } - async execute(stmt: InStatement): Promise { + async execute( + stmt: InStatement, + mode: BatchConfig = {}, + ): Promise { + console.log("mode: ", mode); const streamState = await this.#openStream(); try { const hranaStmt = stmtToHrana(stmt); @@ -148,6 +154,14 @@ export class WsClient implements Client { const hranaRowsPromise = streamState.stream.query(hranaStmt); streamState.stream.closeGracefully(); + console.log("mode.wait: ", mode.wait); + if (mode.wait) { + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + return resultSetFromHrana(await hranaRowsPromise); } catch (e) { throw mapHranaError(e); diff --git a/packages/libsql-core/src/api.ts b/packages/libsql-core/src/api.ts index 22ee7543..34a177ba 100644 --- a/packages/libsql-core/src/api.ts +++ b/packages/libsql-core/src/api.ts @@ -79,7 +79,7 @@ export interface Client { * }); * ``` */ - execute(stmt: InStatement): Promise; + execute(stmt: InStatement, options?: { wait: boolean }): Promise; /** Execute a batch of SQL statements in a transaction. * diff --git a/test.ts b/test.ts index 8d09bd2e..d877fc24 100644 --- a/test.ts +++ b/test.ts @@ -23,7 +23,7 @@ const schemaClient = createClient({ async function main() { await schemaClient.execute( - "ALTER TABLE users ADD COLUMN test_column_8 number;", + "ALTER TABLE users ADD COLUMN test_column_12 number;", { wait: true }, ); } From 443ffb5b610740cea405bbb36723cd823640fb9d Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 27 May 2024 04:00:20 -0400 Subject: [PATCH 18/45] Add msw dependency --- package-lock.json | 326 ++++++++++++++++++++++++++++ packages/libsql-client/package.json | 1 + 2 files changed, 327 insertions(+) diff --git a/package-lock.json b/package-lock.json index a76dbbbc..1a59fb53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -597,6 +597,114 @@ "dev": true, "license": "MIT" }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@inquirer/confirm": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.8.tgz", + "integrity": "sha512-f3INZ+ca4dQdn+MQiq1yP/mOIR/Oc8BLRYuDh6ciToWd6z4W8yArfzjBCMQ0BPY8PcJKwZxGIt8Z6yNT32eSTw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^8.2.1", + "@inquirer/type": "^1.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.1.tgz", + "integrity": "sha512-TIcuQMn2qrtyYe0j136UpHeYpk7AcR/trKeT/7YY0vRgcS9YSfJuQ2+PudPhSofLLsHNnRYAHScQCcVZrJkMqA==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.2", + "@inquirer/type": "^1.3.2", + "@types/mute-stream": "^0.0.4", + "@types/node": "^20.12.12", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.2.tgz", + "integrity": "sha512-4F1MBwVr3c/m4bAUef6LgkvBfSjzwH+OfldgHqcuacWwSUetFebM2wi58WfG9uk1rR98U6GwLed4asLJbwdV5w==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.3.2.tgz", + "integrity": "sha512-5Frickan9c89QbPkSu6I6y8p+9eR6hZkdPahGmNDsTFX8FHLPAozyzCZMKUeW8FyYwnlCKUjqIEqxY+UctARiw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -979,10 +1087,58 @@ "sqlite-wasm": "bin/index.js" } }, + "node_modules/@mswjs/cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.0.tgz", + "integrity": "sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.29.1.tgz", + "integrity": "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==", + "dev": true, + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@neon-rs/load": { "version": "0.0.4", "license": "MIT" }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "dev": true, @@ -1041,6 +1197,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "dev": true, @@ -1079,6 +1241,15 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "18.19.8", "license": "MIT", @@ -1091,6 +1262,18 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.10", "license": "MIT", @@ -1444,6 +1627,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -1505,6 +1700,15 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/cliui": { "version": "8.0.1", "dev": true, @@ -1571,6 +1775,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/create-jest": { "version": "29.7.0", "dev": true, @@ -1933,6 +2146,15 @@ "dev": true, "license": "ISC" }, + "node_modules/graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -1952,6 +2174,12 @@ "node": ">= 0.4" } }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true + }, "node_modules/html-escaper": { "version": "2.0.2", "dev": true, @@ -2051,6 +2279,12 @@ "node": ">=6" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3361,6 +3595,70 @@ "version": "2.1.2", "license": "MIT" }, + "node_modules/msw": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.3.0.tgz", + "integrity": "sha512-cDr1q/QTMzaWhY8n9lpGhceY209k29UZtdTgJ3P8Bzne3TSMchX2EM/ldvn4ATLOktpCefCU2gcEgzHc31GTPw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@inquirer/confirm": "^3.0.0", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.29.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.7.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.18.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.18.3.tgz", + "integrity": "sha512-Q08/0IrpvM+NMY9PA2rti9Jb+JejTddwmwmVQGskAlhtcrw1wsRzoR6ode6mR+OAabNa75w/dxedSUY2mlphaQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "dev": true, @@ -3449,6 +3747,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/outvariant": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", + "dev": true + }, "node_modules/p-limit": { "version": "3.1.0", "dev": true, @@ -3541,6 +3845,12 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dev": true + }, "node_modules/picocolors": { "version": "1.0.0", "dev": true, @@ -3852,6 +4162,21 @@ "node": ">=10" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -4339,6 +4664,7 @@ "husky": "^9.0.11", "jest": "^29.3.1", "lint-staged": "^15.2.2", + "msw": "^2.3.0", "prettier": "3.2.5", "ts-jest": "^29.0.5", "typedoc": "^0.23.28", diff --git a/packages/libsql-client/package.json b/packages/libsql-client/package.json index 259a9a3f..45bf9895 100644 --- a/packages/libsql-client/package.json +++ b/packages/libsql-client/package.json @@ -113,6 +113,7 @@ "husky": "^9.0.11", "jest": "^29.3.1", "lint-staged": "^15.2.2", + "msw": "^2.3.0", "prettier": "3.2.5", "ts-jest": "^29.0.5", "typedoc": "^0.23.28", From 3fc5f5b185ba9a96aa5e7bc970a70fcabd9c6cf7 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 27 May 2024 04:07:41 -0400 Subject: [PATCH 19/45] Update max retries and sleep time to match Go integration --- packages/libsql-client/src/migrations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 7161a9b3..72300abe 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -16,8 +16,8 @@ type MigrationResult = { migrations: Array; }; -const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 5000; -const SCHEMA_MIGRATION_MAX_RETRIES = 6; +const SCHEMA_MIGRATION_SLEEP_TIME_IN_MS = 1000; +const SCHEMA_MIGRATION_MAX_RETRIES = 30; async function sleep(ms: number) { return new Promise((resolve) => { From 8e8b9bf23c34711d7bf4b68cb8f4ad1378a2bd89 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 27 May 2024 04:19:45 -0400 Subject: [PATCH 20/45] Add BatchConfig to the execute type definition and remove it from batch --- packages/libsql-core/src/api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/libsql-core/src/api.ts b/packages/libsql-core/src/api.ts index 34a177ba..f1190a9f 100644 --- a/packages/libsql-core/src/api.ts +++ b/packages/libsql-core/src/api.ts @@ -79,7 +79,7 @@ export interface Client { * }); * ``` */ - execute(stmt: InStatement, options?: { wait: boolean }): Promise; + execute(stmt: InStatement, options?: BatchConfig): Promise; /** Execute a batch of SQL statements in a transaction. * @@ -116,7 +116,7 @@ export interface Client { */ batch( stmts: Array, - mode?: TransactionMode | BatchConfig, + mode?: TransactionMode, ): Promise>; /** Start an interactive transaction. @@ -338,8 +338,8 @@ export interface Transaction { * the previous write transactions to complete. */ export type TransactionMode = "write" | "read" | "deferred"; + export type BatchConfig = { - transactionMode?: TransactionMode; wait?: boolean; }; From 618c556506422d370dd2537094ad3b551f18c4c6 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 27 May 2024 04:21:42 -0400 Subject: [PATCH 21/45] Remove hrana-test-server folder --- packages/libsql-client/hrana-test-server | 1 - 1 file changed, 1 deletion(-) delete mode 160000 packages/libsql-client/hrana-test-server diff --git a/packages/libsql-client/hrana-test-server b/packages/libsql-client/hrana-test-server deleted file mode 160000 index e0044a62..00000000 --- a/packages/libsql-client/hrana-test-server +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e0044a62f4bd34766197122139c89948e1628be4 From c74b863b20a025a482eb92c6299871a2206ee13d Mon Sep 17 00:00:00 2001 From: Giovanni Date: Mon, 27 May 2024 06:24:38 -0400 Subject: [PATCH 22/45] Update package-lock.json from main --- package-lock.json | 182 ++++++++++++++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a59fb53..378ac964 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1049,10 +1049,23 @@ }, "node_modules/@libsql/darwin-arm64": { "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.3.10.tgz", + "integrity": "sha512-RaexEFfPAFogd6dJlqkpCkTxdr6K14Z0286lodIJ8Ny77mWuWyBkWKxf70OYWXXAMxMJFUW+6al1F3/Osf/pTg==", "cpu": [ "arm64" ], - "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/darwin-x64": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.3.10.tgz", + "integrity": "sha512-SNVN6n4qNUdMW1fJMFmx4qn4n5RnXsxjFbczpkzG/V7m/5VeTFt1chhGcrahTHCr3+K6eRJWJUEQHRGqjBwPkw==", + "cpu": [ + "x64" + ], "optional": true, "os": [ "darwin" @@ -1060,7 +1073,8 @@ }, "node_modules/@libsql/hrana-client": { "version": "0.6.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.6.0.tgz", + "integrity": "sha512-k+fqzdjqg3IvWfKmVJK5StsbjeTcyNAXFelUbXbGNz3yH1gEVT9mZ6kmhsIXP30ZSyVV0AE1Gi25p82mxC9hwg==", "dependencies": { "@libsql/isomorphic-fetch": "^0.2.1", "@libsql/isomorphic-ws": "^0.1.5", @@ -1070,7 +1084,8 @@ }, "node_modules/@libsql/isomorphic-fetch": { "version": "0.2.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.2.1.tgz", + "integrity": "sha512-Sv07QP1Aw8A5OOrmKgRUBKe2fFhF2hpGJhtHe3d1aRnTESZCGkn//0zDycMKTGamVWb3oLYRroOsCV8Ukes9GA==" }, "node_modules/@libsql/isomorphic-ws": { "version": "0.1.5", @@ -1082,11 +1097,72 @@ }, "node_modules/@libsql/libsql-wasm-experimental": { "version": "0.0.2", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@libsql/libsql-wasm-experimental/-/libsql-wasm-experimental-0.0.2.tgz", + "integrity": "sha512-xkiu88QwozGr3KEt9h0zeHLYUIWkeDchXmuOUW4/Wh/mRZkDlNtIIePAR0FiLl1j0o4OyTEOtPnvmaXQ5MNTKQ==", "bin": { "sqlite-wasm": "bin/index.js" } }, + "node_modules/@libsql/linux-arm64-gnu": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.3.10.tgz", + "integrity": "sha512-2uXpi9d8qtyIOr7pyG4a88j6YXgemyIHEs2Wbp+PPletlCIPsFS+E7IQHbz8VwTohchOzcokGUm1Bc5QC+A7wg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm64-musl": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.3.10.tgz", + "integrity": "sha512-72SN1FUavLvzHddCS861ynSpQndcW5oLGKA3U8CyMfgIZIwJAPc7+48Uj1plW00htXBx4GBpcntFp68KKIx3YQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-gnu": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.3.10.tgz", + "integrity": "sha512-hXyNqVRi7ONuyWZ1SX6setxL0QaQ7InyS3bHLupsi9s7NpOGD5vcpTaYicJOqmIIm+6kt8vJfmo7ZxlarIHy7Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-musl": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.3.10.tgz", + "integrity": "sha512-kNmIRxomVwt9S+cLyYS497F/3gXFF4r8wW12YSBQgxG75JYft07AHVd8J7HINg+oqRkLzT0s+mVX5dM6nk68EQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/win32-x64-msvc": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.3.10.tgz", + "integrity": "sha512-c/6rjdtGULKrJkLgfLobFefObfOtxjXGmCfPxv6pr0epPCeUEssfDbDIeEH9fQUgzogIMWEHwT8so52UJ/iT1Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@mswjs/cookies": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.0.tgz", @@ -1115,7 +1191,8 @@ }, "node_modules/@neon-rs/load": { "version": "0.0.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", + "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==" }, "node_modules/@open-draft/deferred-promise": { "version": "2.2.0", @@ -1471,11 +1548,10 @@ } }, "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "3.0.2", + "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "fill-range": "^7.0.1" }, "engines": { "node": ">=8" @@ -1758,11 +1834,11 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "engines": { - "node": ">=18" + "node": ">=16" } }, "node_modules/concat-map": { @@ -1861,7 +1937,8 @@ }, "node_modules/detect-libc": { "version": "2.0.2", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "engines": { "node": ">=8" } @@ -2023,9 +2100,8 @@ } }, "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "version": "7.0.1", + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2287,8 +2363,7 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -3027,12 +3102,13 @@ }, "node_modules/libsql": { "version": "0.3.10", + "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.3.10.tgz", + "integrity": "sha512-/8YMTbwWFPmrDWY+YFK3kYqVPFkMgQre0DGmBaOmjogMdSe+7GHm1/q9AZ61AWkEub/vHmi+bA4tqIzVhKnqzg==", "cpu": [ "x64", "arm64", "wasm32" ], - "license": "MIT", "os": [ "darwin", "linux", @@ -3053,14 +3129,11 @@ } }, "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", "engines": { "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -3069,20 +3142,20 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.4.tgz", - "integrity": "sha512-3F9KRQIS2fVDGtCkBp4Bx0jswjX7zUcKx6OF0ZeY1prksUyKPRIIUqZhIUYAstJfvj6i48VFs4dwVIbCYwvTYQ==", + "version": "15.2.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz", + "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", "dependencies": { "chalk": "5.3.0", - "commander": "12.1.0", + "commander": "11.1.0", "debug": "4.3.4", "execa": "8.0.1", - "lilconfig": "3.1.1", - "listr2": "8.2.1", - "micromatch": "4.0.6", + "lilconfig": "3.0.0", + "listr2": "8.0.1", + "micromatch": "4.0.5", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.4.2" + "yaml": "2.3.4" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -3230,15 +3303,15 @@ } }, "node_modules/listr2": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", - "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz", + "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.0.0", - "rfdc": "^1.3.1", + "rfdc": "^1.3.0", "wrap-ansi": "^9.0.0" }, "engines": { @@ -3551,28 +3624,16 @@ "license": "MIT" }, "node_modules/micromatch": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz", - "integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==", + "version": "4.0.5", + "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^4.0.2" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "license": "MIT", @@ -3858,7 +3919,6 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -4298,8 +4358,7 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -4602,12 +4661,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", - "bin": { - "yaml": "bin.mjs" - }, + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "engines": { "node": ">= 14" } From 1f620a94f26dd677fbfa930bd2723a4ff77961a5 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Tue, 28 May 2024 08:01:43 -0400 Subject: [PATCH 23/45] Fix stuck tests by don't running msw on client tests --- packages/libsql-client/src/__tests__/client.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 4bb0895b..c9d4b93a 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -10,11 +10,6 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; import { waitForLastMigrationJobToFinish } from "../migrations"; -import { server as mswServer } from "./mocks/node"; - -beforeAll(() => mswServer.listen()); -afterEach(() => mswServer.resetHandlers()); -afterAll(() => mswServer.close()); jest.mock("../migrations", () => { const originalModule = jest.requireActual("../migrations"); From c63b7c36a1faf8e800b8c56fdca6dc160b46a0cc Mon Sep 17 00:00:00 2001 From: Giovanni Date: Tue, 28 May 2024 08:05:36 -0400 Subject: [PATCH 24/45] Fix integration test for waitForLastMigrationJobToFinish --- .../src/__tests__/client.test.ts | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index c9d4b93a..9738072e 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -10,6 +10,7 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; import { waitForLastMigrationJobToFinish } from "../migrations"; +import { server as mswServer } from "./mocks/node"; jest.mock("../migrations", () => { const originalModule = jest.requireActual("../migrations"); @@ -300,19 +301,28 @@ describe("execute()", () => { test( "calls waitForLastMigrationJobToFinish when the wait parameter is set to true", withClient(async (c) => { - await c.batch( - ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], - "write", - ); - await c.execute("ALTER TABLE t ADD COLUMN another_column number", { - wait: false, - }); - expect(waitForLastMigrationJobToFinish).not.toHaveBeenCalled(); + try { + mswServer.listen(); - await c.execute("ALTER TABLE t ADD COLUMN test_column number", { - wait: true, - }); - expect(waitForLastMigrationJobToFinish).toHaveBeenCalled(); + await c.batch( + ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], + "write", + ); + await c.execute( + "ALTER TABLE t ADD COLUMN another_column number", + { + wait: false, + }, + ); + expect(waitForLastMigrationJobToFinish).not.toHaveBeenCalled(); + + await c.execute("ALTER TABLE t ADD COLUMN test_column number", { + wait: true, + }); + expect(waitForLastMigrationJobToFinish).toHaveBeenCalled(); + } finally { + mswServer.close(); + } }), ); }); From 738e1b0ed03cf82636c1e099f4f6f6de44f05411 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Tue, 28 May 2024 08:30:24 -0400 Subject: [PATCH 25/45] Skip test that test a call to waitForLastMigrationJobToFinish for local files --- packages/libsql-client/src/__tests__/client.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 9738072e..5cf87557 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -298,7 +298,7 @@ describe("execute()", () => { }), ); - test( + (!isFile ? test : test.skip)( "calls waitForLastMigrationJobToFinish when the wait parameter is set to true", withClient(async (c) => { try { From 2abb9fe17d653d9911bcfb74495ce2fd55039bd6 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Tue, 28 May 2024 08:39:04 -0400 Subject: [PATCH 26/45] Fix migrations file import --- packages/libsql-client/src/http.ts | 2 +- packages/libsql-client/src/ws.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 26e94278..7d23fff1 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -21,7 +21,7 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; -import { waitForLastMigrationJobToFinish } from "./migrations"; +import { waitForLastMigrationJobToFinish } from "./migrations.js"; export * from "@libsql/core/api"; diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index b118d5f3..19267927 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -22,7 +22,7 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; -import { waitForLastMigrationJobToFinish } from "./migrations"; +import { waitForLastMigrationJobToFinish } from "./migrations.js"; export * from "@libsql/core/api"; From ed929b07b2f609010522acc8f6652112b0d9720e Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 09:05:14 -0400 Subject: [PATCH 27/45] Automatically detect if database is a schema database when calling execute --- .gitignore | 1 + .../src/__tests__/client.test.ts | 39 ------------------- packages/libsql-client/src/http.ts | 17 +++----- packages/libsql-client/src/migrations.ts | 34 ++++++++++++++++ packages/libsql-client/src/ws.ts | 18 +++------ packages/libsql-core/src/api.ts | 6 +-- test.ts | 11 ++++-- 7 files changed, 54 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index 60688f18..5e61a70e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules /docs *.tsbuildinfo Session.vim +packages/libsql-client/hrana-test-server diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 5cf87557..7d230e96 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -9,17 +9,6 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; -import { waitForLastMigrationJobToFinish } from "../migrations"; -import { server as mswServer } from "./mocks/node"; - -jest.mock("../migrations", () => { - const originalModule = jest.requireActual("../migrations"); - - return { - ...originalModule, - waitForLastMigrationJobToFinish: jest.fn(), - }; -}); const config = { url: process.env.URL ?? "ws://localhost:8080", @@ -297,34 +286,6 @@ describe("execute()", () => { expect(Array.from(selectRs.rows[0])).toStrictEqual(["three"]); }), ); - - (!isFile ? test : test.skip)( - "calls waitForLastMigrationJobToFinish when the wait parameter is set to true", - withClient(async (c) => { - try { - mswServer.listen(); - - await c.batch( - ["DROP TABLE IF EXISTS t", "CREATE TABLE t (a)"], - "write", - ); - await c.execute( - "ALTER TABLE t ADD COLUMN another_column number", - { - wait: false, - }, - ); - expect(waitForLastMigrationJobToFinish).not.toHaveBeenCalled(); - - await c.execute("ALTER TABLE t ADD COLUMN test_column number", { - wait: true, - }); - expect(waitForLastMigrationJobToFinish).toHaveBeenCalled(); - } finally { - mswServer.close(); - } - }), - ); }); describe("values", () => { diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 7d23fff1..0083e7b1 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -2,7 +2,6 @@ import * as hrana from "@libsql/hrana-client"; import type { Config, Client } from "@libsql/core/api"; import type { - BatchConfig, InStatement, ResultSet, Transaction, @@ -84,11 +83,7 @@ export class HttpClient implements Client { this.#authToken = authToken; } - async execute( - stmt: InStatement, - mode: BatchConfig = {}, - ): Promise { - console.log("mode: ", mode); + async execute(stmt: InStatement): Promise { try { const hranaStmt = stmtToHrana(stmt); @@ -102,12 +97,10 @@ export class HttpClient implements Client { stream.closeGracefully(); } - if (mode.wait) { - await waitForLastMigrationJobToFinish({ - authToken: this.#authToken, - baseUrl: this.#url.origin, - }); - } + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); return resultSetFromHrana(await rowsPromise); } catch (e) { diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 72300abe..5b2b84c8 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -65,11 +65,36 @@ type getLastMigrationJobProps = { baseUrl: string; }; +export async function getIsSchemaDatabase({ + authToken, + baseUrl, +}: { + authToken: string | undefined; + baseUrl: string; +}) { + const url = baseUrl + "/v1/jobs"; + const result = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${authToken}`, + }, + }); + const json = (await result.json()) as { error: string }; + const isChildDatabase = + result.status === 400 && json.error === "Invalid namespace"; + return !isChildDatabase; +} + async function getLastMigrationJob({ authToken, baseUrl, }: getLastMigrationJobProps): Promise { const url = baseUrl + "/v1/jobs"; + const isSchemaDatabase = await getIsSchemaDatabase({ + authToken, + baseUrl, + }); + console.log("isSchemaDatabase: ", isSchemaDatabase); const result = await fetch(url, { method: "GET", headers: { @@ -115,6 +140,15 @@ export async function waitForLastMigrationJobToFinish({ authToken, baseUrl, }: getLastMigrationJobProps) { + const isSchemaDatabase = await getIsSchemaDatabase({ + authToken, + baseUrl, + }); + console.log("isSchemaDatabase: ", isSchemaDatabase); + if (!isSchemaDatabase) { + return; + } + console.log("Waiting for migration jobs"); const lastMigrationJob = await getLastMigrationJob({ authToken: authToken, diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 19267927..cade4c05 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -1,7 +1,6 @@ import * as hrana from "@libsql/hrana-client"; import type { - BatchConfig, Config, IntMode, Client, @@ -139,11 +138,7 @@ export class WsClient implements Client { this.protocol = "ws"; } - async execute( - stmt: InStatement, - mode: BatchConfig = {}, - ): Promise { - console.log("mode: ", mode); + async execute(stmt: InStatement): Promise { const streamState = await this.#openStream(); try { const hranaStmt = stmtToHrana(stmt); @@ -154,13 +149,10 @@ export class WsClient implements Client { const hranaRowsPromise = streamState.stream.query(hranaStmt); streamState.stream.closeGracefully(); - console.log("mode.wait: ", mode.wait); - if (mode.wait) { - await waitForLastMigrationJobToFinish({ - authToken: this.#authToken, - baseUrl: this.#url.origin, - }); - } + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); return resultSetFromHrana(await hranaRowsPromise); } catch (e) { diff --git a/packages/libsql-core/src/api.ts b/packages/libsql-core/src/api.ts index f1190a9f..6278fc03 100644 --- a/packages/libsql-core/src/api.ts +++ b/packages/libsql-core/src/api.ts @@ -79,7 +79,7 @@ export interface Client { * }); * ``` */ - execute(stmt: InStatement, options?: BatchConfig): Promise; + execute(stmt: InStatement): Promise; /** Execute a batch of SQL statements in a transaction. * @@ -339,10 +339,6 @@ export interface Transaction { */ export type TransactionMode = "write" | "read" | "deferred"; -export type BatchConfig = { - wait?: boolean; -}; - /** Result of executing an SQL statement. * * ```javascript diff --git a/test.ts b/test.ts index d877fc24..1e6acb48 100644 --- a/test.ts +++ b/test.ts @@ -10,7 +10,13 @@ const schemaUrl = "libsql://schema-test-giovannibenussi.turso.io"; const schemaAuthToken = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODIwMTAsImlkIjoiM2IyYTIwMDEtOTcxZC00MzIzLWE2YWYtMjk1YTRmOWNkYzVkIn0.l-LzYur2KffpkrZog5vT3eThwB3m2Nl0RIgc5rLn1DpBsYyWujPTkpS62WoYBwWbM0AMaAoRqfyCzi-T-LnJBQ"; -const client = createClient({ +const normalClient = createClient({ + url: "libsql://libsql-client-test-giovannibenussi.turso.io", + authToken: + "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTY5ODc2MDgsImlkIjoiNjhlNzI2M2ItNGQxMi00NWJhLWIxYWYtZGRjZmFhN2I5MTY0In0.PCMMgoOVxnh-Aj10urzsMpXH1anzcmng_Q0ByXIz_E9zHMZide3NAgeVzDg52q3DgHbMndoid_qv1ULjsChMAg", +}); + +const childClient = createClient({ url: "libsql://schema-child-1-giovannibenussi.turso.io", authToken: "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTU2ODE0MDIsImlkIjoiYjJhYjRhNjQtNDAyYy00YmRmLWExZTgtMjdlZjMzNTE4Y2JkIn0.Og9E7nl_Y8P93FO1XJlvAhkKEOsGynDdFEziJwLeGrMNaAOhQLqdxk7shao13VQo4JVFkMuSTXMibKXuPnavBA", @@ -23,8 +29,7 @@ const schemaClient = createClient({ async function main() { await schemaClient.execute( - "ALTER TABLE users ADD COLUMN test_column_12 number;", - { wait: true }, + "ALTER TABLE users ADD COLUMN test_column_17 number;", ); } From e908bec4abc17147d605640f212d0db6219b7a73 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 09:22:14 -0400 Subject: [PATCH 28/45] Add cache to getIsSchemaDatabase --- packages/libsql-client/src/http.ts | 28 +++++++++++++++++++----- packages/libsql-client/src/migrations.ts | 14 ------------ packages/libsql-client/src/ws.ts | 28 +++++++++++++++++++----- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 0083e7b1..b54281d9 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -20,7 +20,10 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; -import { waitForLastMigrationJobToFinish } from "./migrations.js"; +import { + getIsSchemaDatabase, + waitForLastMigrationJobToFinish, +} from "./migrations.js"; export * from "@libsql/core/api"; @@ -68,6 +71,7 @@ export class HttpClient implements Client { protocol: "http"; #url: URL; #authToken: string | undefined; + #isSchemaDatabase: boolean | undefined; /** @private */ constructor( @@ -83,6 +87,17 @@ export class HttpClient implements Client { this.#authToken = authToken; } + async getIsSchemaDatabase(): Promise { + if (this.#isSchemaDatabase === undefined) { + this.#isSchemaDatabase = await getIsSchemaDatabase({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + + return this.#isSchemaDatabase; + } + async execute(stmt: InStatement): Promise { try { const hranaStmt = stmtToHrana(stmt); @@ -97,10 +112,13 @@ export class HttpClient implements Client { stream.closeGracefully(); } - await waitForLastMigrationJobToFinish({ - authToken: this.#authToken, - baseUrl: this.#url.origin, - }); + const isSchemaDatabase = await this.getIsSchemaDatabase(); + if (isSchemaDatabase) { + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } return resultSetFromHrana(await rowsPromise); } catch (e) { diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 5b2b84c8..85b4c832 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -90,11 +90,6 @@ async function getLastMigrationJob({ baseUrl, }: getLastMigrationJobProps): Promise { const url = baseUrl + "/v1/jobs"; - const isSchemaDatabase = await getIsSchemaDatabase({ - authToken, - baseUrl, - }); - console.log("isSchemaDatabase: ", isSchemaDatabase); const result = await fetch(url, { method: "GET", headers: { @@ -140,15 +135,6 @@ export async function waitForLastMigrationJobToFinish({ authToken, baseUrl, }: getLastMigrationJobProps) { - const isSchemaDatabase = await getIsSchemaDatabase({ - authToken, - baseUrl, - }); - console.log("isSchemaDatabase: ", isSchemaDatabase); - if (!isSchemaDatabase) { - return; - } - console.log("Waiting for migration jobs"); const lastMigrationJob = await getLastMigrationJob({ authToken: authToken, diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index cade4c05..4c054e95 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -21,7 +21,10 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; -import { waitForLastMigrationJobToFinish } from "./migrations.js"; +import { + getIsSchemaDatabase, + waitForLastMigrationJobToFinish, +} from "./migrations.js"; export * from "@libsql/core/api"; @@ -121,6 +124,7 @@ export class WsClient implements Client { #futureConnState: ConnState | undefined; closed: boolean; protocol: "ws"; + #isSchemaDatabase: boolean | undefined; /** @private */ constructor( @@ -138,6 +142,17 @@ export class WsClient implements Client { this.protocol = "ws"; } + async getIsSchemaDatabase(): Promise { + if (this.#isSchemaDatabase === undefined) { + this.#isSchemaDatabase = await getIsSchemaDatabase({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + + return this.#isSchemaDatabase; + } + async execute(stmt: InStatement): Promise { const streamState = await this.#openStream(); try { @@ -149,10 +164,13 @@ export class WsClient implements Client { const hranaRowsPromise = streamState.stream.query(hranaStmt); streamState.stream.closeGracefully(); - await waitForLastMigrationJobToFinish({ - authToken: this.#authToken, - baseUrl: this.#url.origin, - }); + const isSchemaDatabase = await this.getIsSchemaDatabase(); + if (isSchemaDatabase) { + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } return resultSetFromHrana(await hranaRowsPromise); } catch (e) { From 27bea93aee80d10faf5188e37a0a47068bbe7948 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 09:30:31 -0400 Subject: [PATCH 29/45] Run API call to check if database is schema in parallel with query --- packages/libsql-client/src/http.ts | 3 ++- packages/libsql-client/src/ws.ts | 3 ++- test.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index b54281d9..d6eecd18 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -100,6 +100,7 @@ export class HttpClient implements Client { async execute(stmt: InStatement): Promise { try { + const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmt = stmtToHrana(stmt); // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the statement and @@ -112,7 +113,7 @@ export class HttpClient implements Client { stream.closeGracefully(); } - const isSchemaDatabase = await this.getIsSchemaDatabase(); + const isSchemaDatabase = await isSchemaDatabasePromise; if (isSchemaDatabase) { await waitForLastMigrationJobToFinish({ authToken: this.#authToken, diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 4c054e95..25ff3234 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -156,6 +156,7 @@ export class WsClient implements Client { async execute(stmt: InStatement): Promise { const streamState = await this.#openStream(); try { + const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmt = stmtToHrana(stmt); // Schedule all operations synchronously, so they will be pipelined and executed in a single @@ -164,7 +165,7 @@ export class WsClient implements Client { const hranaRowsPromise = streamState.stream.query(hranaStmt); streamState.stream.closeGracefully(); - const isSchemaDatabase = await this.getIsSchemaDatabase(); + const isSchemaDatabase = await isSchemaDatabasePromise; if (isSchemaDatabase) { await waitForLastMigrationJobToFinish({ authToken: this.#authToken, diff --git a/test.ts b/test.ts index 1e6acb48..1a44e73f 100644 --- a/test.ts +++ b/test.ts @@ -29,7 +29,7 @@ const schemaClient = createClient({ async function main() { await schemaClient.execute( - "ALTER TABLE users ADD COLUMN test_column_17 number;", + "ALTER TABLE users ADD COLUMN test_column_18 number;", ); } From d9edd87342aa543868b145a9dc2d88a48f94de7a Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 09:35:18 -0400 Subject: [PATCH 30/45] Wait for results before check for schema migrations --- packages/libsql-client/src/http.ts | 3 ++- packages/libsql-client/src/ws.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index d6eecd18..0366d078 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -113,6 +113,7 @@ export class HttpClient implements Client { stream.closeGracefully(); } + const rowsResult = await rowsPromise; const isSchemaDatabase = await isSchemaDatabasePromise; if (isSchemaDatabase) { await waitForLastMigrationJobToFinish({ @@ -121,7 +122,7 @@ export class HttpClient implements Client { }); } - return resultSetFromHrana(await rowsPromise); + return resultSetFromHrana(rowsResult); } catch (e) { throw mapHranaError(e); } diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 25ff3234..faf19299 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -165,6 +165,7 @@ export class WsClient implements Client { const hranaRowsPromise = streamState.stream.query(hranaStmt); streamState.stream.closeGracefully(); + const hranaRowsResult = await hranaRowsPromise; const isSchemaDatabase = await isSchemaDatabasePromise; if (isSchemaDatabase) { await waitForLastMigrationJobToFinish({ @@ -173,7 +174,7 @@ export class WsClient implements Client { }); } - return resultSetFromHrana(await hranaRowsPromise); + return resultSetFromHrana(hranaRowsResult); } catch (e) { throw mapHranaError(e); } finally { From fa49b5185fb909802875cf206075fdf980d28837 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 09:42:25 -0400 Subject: [PATCH 31/45] Fix tests --- .../libsql-client/src/__tests__/client.test.ts | 5 +++++ .../libsql-client/src/__tests__/mocks/handlers.ts | 15 +++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 7d230e96..99662b3e 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -9,6 +9,11 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; +import { server as mswServer } from "./mocks/node"; + +beforeAll(() => mswServer.listen()); +afterEach(() => mswServer.resetHandlers()); +afterAll(() => mswServer.close()); const config = { url: process.env.URL ?? "ws://localhost:8080", diff --git a/packages/libsql-client/src/__tests__/mocks/handlers.ts b/packages/libsql-client/src/__tests__/mocks/handlers.ts index 0012e6de..dbc98627 100644 --- a/packages/libsql-client/src/__tests__/mocks/handlers.ts +++ b/packages/libsql-client/src/__tests__/mocks/handlers.ts @@ -1,16 +1,11 @@ import { http, HttpResponse } from "msw"; export const handlers = [ - http.get("http://fake-base-url.example.com/v1/jobs", () => { - return HttpResponse.json({ - schema_version: 4, - migrations: [ - { job_id: 4, status: "WaitingDryRun" }, - { job_id: 3, status: "RunSuccess" }, - { job_id: 2, status: "RunSuccess" }, - { job_id: 1, status: "RunSuccess" }, - ], - }); + http.get("ws://localhost:8080/v1/jobs", () => { + return HttpResponse.json( + { error: "Invalid namespace" }, + { status: 400 }, + ); }), http.get( From 799ac3977db3da162ce8e494d55331a31b8e95e1 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 09:52:37 -0400 Subject: [PATCH 32/45] Fix tests --- .../libsql-client/src/__tests__/mocks/handlers.ts | 12 ++++++++++++ packages/libsql-client/src/http.ts | 12 +++++++++++- packages/libsql-client/src/migrations.ts | 1 + packages/libsql-client/src/ws.ts | 12 +++++++++++- test.ts | 9 ++++++--- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/packages/libsql-client/src/__tests__/mocks/handlers.ts b/packages/libsql-client/src/__tests__/mocks/handlers.ts index dbc98627..6364ab55 100644 --- a/packages/libsql-client/src/__tests__/mocks/handlers.ts +++ b/packages/libsql-client/src/__tests__/mocks/handlers.ts @@ -8,6 +8,18 @@ export const handlers = [ ); }), + http.get("http://fake-base-url.example.com/v1/jobs", () => { + return HttpResponse.json({ + schema_version: 4, + migrations: [ + { job_id: 4, status: "WaitingDryRun" }, + { job_id: 3, status: "RunSuccess" }, + { job_id: 2, status: "RunSuccess" }, + { job_id: 1, status: "RunSuccess" }, + ], + }); + }), + http.get( "http://fake-base-url.example.com/v1/jobs/:job_id", ({ params }) => { diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 0366d078..d6177e89 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -133,6 +133,7 @@ export class HttpClient implements Client { mode: TransactionMode = "deferred", ): Promise> { try { + const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmts = stmts.map(stmtToHrana); const version = await this.#client.getVersion(); @@ -161,7 +162,16 @@ export class HttpClient implements Client { stream.closeGracefully(); } - return await resultsPromise; + const results = await resultsPromise; + const isSchemaDatabase = await isSchemaDatabasePromise; + if (isSchemaDatabase) { + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + + return results; } catch (e) { throw mapHranaError(e); } diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 85b4c832..e81ebbbc 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -90,6 +90,7 @@ async function getLastMigrationJob({ baseUrl, }: getLastMigrationJobProps): Promise { const url = baseUrl + "/v1/jobs"; + console.log("url: ", url); const result = await fetch(url, { method: "GET", headers: { diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index faf19299..d628a598 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -188,6 +188,7 @@ export class WsClient implements Client { ): Promise> { const streamState = await this.#openStream(); try { + const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmts = stmts.map(stmtToHrana); const version = await streamState.conn.client.getVersion(); @@ -202,7 +203,16 @@ export class WsClient implements Client { hranaStmts, ); - return await resultsPromise; + const results = await resultsPromise; + const isSchemaDatabase = await isSchemaDatabasePromise; + if (isSchemaDatabase) { + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + + return results; } catch (e) { throw mapHranaError(e); } finally { diff --git a/test.ts b/test.ts index 1a44e73f..6b6715ec 100644 --- a/test.ts +++ b/test.ts @@ -28,9 +28,12 @@ const schemaClient = createClient({ }); async function main() { - await schemaClient.execute( - "ALTER TABLE users ADD COLUMN test_column_18 number;", - ); + //await schemaClient.execute( + //"ALTER TABLE users ADD COLUMN test_column_18 number;", + //); + await schemaClient.batch([ + "ALTER TABLE users ADD COLUMN test_column_19 number;", + ]); } main(); From d5949a7c42dba2f3a39e0c6dde419a384e9919e7 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 10:31:35 -0400 Subject: [PATCH 33/45] Add canary schema test --- test.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test.ts b/test.ts index 6b6715ec..a316f5c4 100644 --- a/test.ts +++ b/test.ts @@ -27,12 +27,25 @@ const schemaClient = createClient({ authToken: schemaAuthToken, }); +const canarySchemaClient = createClient({ + url: "libsql://canary-schema-db-giovannibenussi.turso.io", + authToken: + "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MTY5OTI0MTksImlkIjoiOWM5NjYwZDYtYjFlZS00MDZhLTg1NDYtMzJlMGI2YzE5OTcwIn0.qtQKo32Jtxa8ghhDD1WsN0gbY4kCQMUVgaoJVGNoRehm3XUpsG2z8xzQ4Qgo9r2-GKYE6vmG9ufFGwkoK7shBg", +}); + async function main() { //await schemaClient.execute( //"ALTER TABLE users ADD COLUMN test_column_18 number;", //); - await schemaClient.batch([ - "ALTER TABLE users ADD COLUMN test_column_19 number;", + //await schemaClient.batch([ + //"ALTER TABLE users ADD COLUMN test_column_20 number;", + //]); + //await canarySchemaClient.execute( + //"ALTER TABLE users ADD COLUMN test_column_21 number;", + //); + await canarySchemaClient.batch([ + "ALTER TABLE users ADD COLUMN test_column_101 number;", + "ALTER TABLE users ADD COLUMN test_column_102 number;", ]); } From bd7e80e8f38c4502953d73d8a5dc9220d7ee4573 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 11:14:12 -0400 Subject: [PATCH 34/45] Fix stuck tests --- packages/libsql-client/src/__tests__/client.test.ts | 8 ++++---- packages/libsql-client/src/__tests__/mocks/handlers.ts | 7 ------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/libsql-client/src/__tests__/client.test.ts b/packages/libsql-client/src/__tests__/client.test.ts index 99662b3e..4e59c08b 100644 --- a/packages/libsql-client/src/__tests__/client.test.ts +++ b/packages/libsql-client/src/__tests__/client.test.ts @@ -9,11 +9,11 @@ import "./helpers.js"; import type * as libsql from "../node.js"; import { createClient } from "../node.js"; -import { server as mswServer } from "./mocks/node"; +import * as migrations from "../migrations"; -beforeAll(() => mswServer.listen()); -afterEach(() => mswServer.resetHandlers()); -afterAll(() => mswServer.close()); +jest.spyOn(migrations, "getIsSchemaDatabase").mockImplementation( + (_params) => new Promise((resolve) => resolve(false)), +); const config = { url: process.env.URL ?? "ws://localhost:8080", diff --git a/packages/libsql-client/src/__tests__/mocks/handlers.ts b/packages/libsql-client/src/__tests__/mocks/handlers.ts index 6364ab55..0012e6de 100644 --- a/packages/libsql-client/src/__tests__/mocks/handlers.ts +++ b/packages/libsql-client/src/__tests__/mocks/handlers.ts @@ -1,13 +1,6 @@ import { http, HttpResponse } from "msw"; export const handlers = [ - http.get("ws://localhost:8080/v1/jobs", () => { - return HttpResponse.json( - { error: "Invalid namespace" }, - { status: 400 }, - ); - }), - http.get("http://fake-base-url.example.com/v1/jobs", () => { return HttpResponse.json({ schema_version: 4, From e60f1b6b34a491af5982471c6ad61595079c7b33 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 11:23:26 -0400 Subject: [PATCH 35/45] Remove schema changes from the ws module --- packages/libsql-client/src/ws.ts | 40 ++------------------------------ 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index d628a598..8b5f0c00 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -21,10 +21,6 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; -import { - getIsSchemaDatabase, - waitForLastMigrationJobToFinish, -} from "./migrations.js"; export * from "@libsql/core/api"; @@ -124,7 +120,6 @@ export class WsClient implements Client { #futureConnState: ConnState | undefined; closed: boolean; protocol: "ws"; - #isSchemaDatabase: boolean | undefined; /** @private */ constructor( @@ -142,21 +137,9 @@ export class WsClient implements Client { this.protocol = "ws"; } - async getIsSchemaDatabase(): Promise { - if (this.#isSchemaDatabase === undefined) { - this.#isSchemaDatabase = await getIsSchemaDatabase({ - authToken: this.#authToken, - baseUrl: this.#url.origin, - }); - } - - return this.#isSchemaDatabase; - } - async execute(stmt: InStatement): Promise { const streamState = await this.#openStream(); try { - const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmt = stmtToHrana(stmt); // Schedule all operations synchronously, so they will be pipelined and executed in a single @@ -165,16 +148,7 @@ export class WsClient implements Client { const hranaRowsPromise = streamState.stream.query(hranaStmt); streamState.stream.closeGracefully(); - const hranaRowsResult = await hranaRowsPromise; - const isSchemaDatabase = await isSchemaDatabasePromise; - if (isSchemaDatabase) { - await waitForLastMigrationJobToFinish({ - authToken: this.#authToken, - baseUrl: this.#url.origin, - }); - } - - return resultSetFromHrana(hranaRowsResult); + return resultSetFromHrana(await hranaRowsPromise); } catch (e) { throw mapHranaError(e); } finally { @@ -188,7 +162,6 @@ export class WsClient implements Client { ): Promise> { const streamState = await this.#openStream(); try { - const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmts = stmts.map(stmtToHrana); const version = await streamState.conn.client.getVersion(); @@ -203,16 +176,7 @@ export class WsClient implements Client { hranaStmts, ); - const results = await resultsPromise; - const isSchemaDatabase = await isSchemaDatabasePromise; - if (isSchemaDatabase) { - await waitForLastMigrationJobToFinish({ - authToken: this.#authToken, - baseUrl: this.#url.origin, - }); - } - - return results; + return await resultsPromise; } catch (e) { throw mapHranaError(e); } finally { From 63dd2bae7c7a662db13443eef62a9c90d7fce096 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 11:27:08 -0400 Subject: [PATCH 36/45] Revert "Remove schema changes from the ws module" This reverts commit e60f1b6b34a491af5982471c6ad61595079c7b33. --- packages/libsql-client/src/ws.ts | 40 ++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/libsql-client/src/ws.ts b/packages/libsql-client/src/ws.ts index 8b5f0c00..d628a598 100644 --- a/packages/libsql-client/src/ws.ts +++ b/packages/libsql-client/src/ws.ts @@ -21,6 +21,10 @@ import { import { SqlCache } from "./sql_cache.js"; import { encodeBaseUrl } from "@libsql/core/uri"; import { supportedUrlLink } from "@libsql/core/util"; +import { + getIsSchemaDatabase, + waitForLastMigrationJobToFinish, +} from "./migrations.js"; export * from "@libsql/core/api"; @@ -120,6 +124,7 @@ export class WsClient implements Client { #futureConnState: ConnState | undefined; closed: boolean; protocol: "ws"; + #isSchemaDatabase: boolean | undefined; /** @private */ constructor( @@ -137,9 +142,21 @@ export class WsClient implements Client { this.protocol = "ws"; } + async getIsSchemaDatabase(): Promise { + if (this.#isSchemaDatabase === undefined) { + this.#isSchemaDatabase = await getIsSchemaDatabase({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + + return this.#isSchemaDatabase; + } + async execute(stmt: InStatement): Promise { const streamState = await this.#openStream(); try { + const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmt = stmtToHrana(stmt); // Schedule all operations synchronously, so they will be pipelined and executed in a single @@ -148,7 +165,16 @@ export class WsClient implements Client { const hranaRowsPromise = streamState.stream.query(hranaStmt); streamState.stream.closeGracefully(); - return resultSetFromHrana(await hranaRowsPromise); + const hranaRowsResult = await hranaRowsPromise; + const isSchemaDatabase = await isSchemaDatabasePromise; + if (isSchemaDatabase) { + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + + return resultSetFromHrana(hranaRowsResult); } catch (e) { throw mapHranaError(e); } finally { @@ -162,6 +188,7 @@ export class WsClient implements Client { ): Promise> { const streamState = await this.#openStream(); try { + const isSchemaDatabasePromise = this.getIsSchemaDatabase(); const hranaStmts = stmts.map(stmtToHrana); const version = await streamState.conn.client.getVersion(); @@ -176,7 +203,16 @@ export class WsClient implements Client { hranaStmts, ); - return await resultsPromise; + const results = await resultsPromise; + const isSchemaDatabase = await isSchemaDatabasePromise; + if (isSchemaDatabase) { + await waitForLastMigrationJobToFinish({ + authToken: this.#authToken, + baseUrl: this.#url.origin, + }); + } + + return results; } catch (e) { throw mapHranaError(e); } finally { From b866c086ca879bd4676fd498497775448b5ed140 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 11:40:41 -0400 Subject: [PATCH 37/45] Normalize URL to know if a database is a schema database to https --- packages/libsql-client/src/migrations.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index e81ebbbc..0d704bc3 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -65,6 +65,17 @@ type getLastMigrationJobProps = { baseUrl: string; }; +function urlToHttps(url: string) { + const PREFIXES = ["ws://", "http://", "wss://"]; + for (const prefix of PREFIXES) { + if (url.startsWith(prefix)) { + return url.replace(prefix, "https://"); + } + } + + return url; +} + export async function getIsSchemaDatabase({ authToken, baseUrl, @@ -72,7 +83,7 @@ export async function getIsSchemaDatabase({ authToken: string | undefined; baseUrl: string; }) { - const url = baseUrl + "/v1/jobs"; + const url = urlToHttps(baseUrl + "/v1/jobs"); const result = await fetch(url, { method: "GET", headers: { From 9ed7179b111832a3f665c1eb55ad45c4c09e5376 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 11:51:24 -0400 Subject: [PATCH 38/45] Fix tests --- packages/libsql-client/src/migrations.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 0d704bc3..cd8a7701 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -65,12 +65,12 @@ type getLastMigrationJobProps = { baseUrl: string; }; -function urlToHttps(url: string) { - const PREFIXES = ["ws://", "http://", "wss://"]; - for (const prefix of PREFIXES) { - if (url.startsWith(prefix)) { - return url.replace(prefix, "https://"); - } +function normalizeURLScheme(url: string) { + if (url.startsWith("ws://")) { + return url.replace("ws://", "http://"); + } + if (url.startsWith("wss://")) { + return url.replace("wss://", "https://"); } return url; @@ -83,7 +83,7 @@ export async function getIsSchemaDatabase({ authToken: string | undefined; baseUrl: string; }) { - const url = urlToHttps(baseUrl + "/v1/jobs"); + const url = normalizeURLScheme(baseUrl + "/v1/jobs"); const result = await fetch(url, { method: "GET", headers: { From 670d6440a3befc1edac1c5fc23fa37cc07f33043 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 11:52:09 -0400 Subject: [PATCH 39/45] Remove console.log from migrations file --- packages/libsql-client/src/migrations.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index cd8a7701..7941d0b8 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -37,7 +37,6 @@ async function isMigrationJobFinished({ jobId, }: isMigrationJobFinishedProps): Promise { const url = baseUrl + `/v1/jobs/${jobId}`; - console.log("isMigrationJobFinished url:", url); const result = await fetch(url, { method: "GET", headers: { @@ -45,7 +44,6 @@ async function isMigrationJobFinished({ }, }); const json = (await result.json()) as ExtendedMigrationJobType; - console.log("json:", json); const job = json as { status: string }; if (result.status !== 200) { throw new Error( @@ -101,7 +99,6 @@ async function getLastMigrationJob({ baseUrl, }: getLastMigrationJobProps): Promise { const url = baseUrl + "/v1/jobs"; - console.log("url: ", url); const result = await fetch(url, { method: "GET", headers: { @@ -116,7 +113,6 @@ async function getLastMigrationJob({ } const json = (await result.json()) as MigrationResult; - console.log("json:", json); if (!json.migrations || json.migrations.length === 0) { throw new Error("No migrations found"); } @@ -147,29 +143,19 @@ export async function waitForLastMigrationJobToFinish({ authToken, baseUrl, }: getLastMigrationJobProps) { - console.log("Waiting for migration jobs"); const lastMigrationJob = await getLastMigrationJob({ authToken: authToken, baseUrl, }); - console.log("lastMigrationJob:", lastMigrationJob); if (lastMigrationJob.status !== "RunSuccess") { let i = 0; while (i < SCHEMA_MIGRATION_MAX_RETRIES) { i++; - console.log( - `${i}: Waiting for migration job to finish, attempt:`, - i, - ); const isLastMigrationJobFinished = await isMigrationJobFinished({ authToken: authToken, baseUrl, jobId: lastMigrationJob.job_id, }); - console.log( - "isLastMigrationJobFinished:", - isLastMigrationJobFinished, - ); if (isLastMigrationJobFinished) { break; } From ce3e89d96a3ff80ba7e45b7231e11fcfdafe8a59 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 11:56:29 -0400 Subject: [PATCH 40/45] Add debug logs to migrations file --- packages/libsql-client/src/migrations.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 7941d0b8..3be04046 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -82,12 +82,14 @@ export async function getIsSchemaDatabase({ baseUrl: string; }) { const url = normalizeURLScheme(baseUrl + "/v1/jobs"); + console.log("url: ", url); const result = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${authToken}`, }, }); + console.log("result: ", result); const json = (await result.json()) as { error: string }; const isChildDatabase = result.status === 400 && json.error === "Invalid namespace"; From 3183e9389dbb38bff9333ae8b41990179e186b11 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 12:02:11 -0400 Subject: [PATCH 41/45] Normalize ws to https in normalizeURLScheme --- packages/libsql-client/src/migrations.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 3be04046..1c39e0d6 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -65,7 +65,7 @@ type getLastMigrationJobProps = { function normalizeURLScheme(url: string) { if (url.startsWith("ws://")) { - return url.replace("ws://", "http://"); + return url.replace("ws://", "https://"); } if (url.startsWith("wss://")) { return url.replace("wss://", "https://"); @@ -81,6 +81,7 @@ export async function getIsSchemaDatabase({ authToken: string | undefined; baseUrl: string; }) { + console.log("baseUrl: ", baseUrl); const url = normalizeURLScheme(baseUrl + "/v1/jobs"); console.log("url: ", url); const result = await fetch(url, { From 4f1c17153cd99eb7339d801bc2c4c3b4b5fc3e77 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 12:07:28 -0400 Subject: [PATCH 42/45] Revert "Normalize ws to https in normalizeURLScheme" This reverts commit 3183e9389dbb38bff9333ae8b41990179e186b11. --- packages/libsql-client/src/migrations.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 1c39e0d6..3be04046 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -65,7 +65,7 @@ type getLastMigrationJobProps = { function normalizeURLScheme(url: string) { if (url.startsWith("ws://")) { - return url.replace("ws://", "https://"); + return url.replace("ws://", "http://"); } if (url.startsWith("wss://")) { return url.replace("wss://", "https://"); @@ -81,7 +81,6 @@ export async function getIsSchemaDatabase({ authToken: string | undefined; baseUrl: string; }) { - console.log("baseUrl: ", baseUrl); const url = normalizeURLScheme(baseUrl + "/v1/jobs"); console.log("url: ", url); const result = await fetch(url, { From c9d4bdb603cfc98a6b012db14851294cbb01f170 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 12:11:35 -0400 Subject: [PATCH 43/45] Normalize URLs for migration jobs --- packages/libsql-client/src/migrations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/libsql-client/src/migrations.ts b/packages/libsql-client/src/migrations.ts index 3be04046..0276dc50 100644 --- a/packages/libsql-client/src/migrations.ts +++ b/packages/libsql-client/src/migrations.ts @@ -36,7 +36,7 @@ async function isMigrationJobFinished({ baseUrl, jobId, }: isMigrationJobFinishedProps): Promise { - const url = baseUrl + `/v1/jobs/${jobId}`; + const url = normalizeURLScheme(baseUrl + `/v1/jobs/${jobId}`); const result = await fetch(url, { method: "GET", headers: { @@ -100,7 +100,7 @@ async function getLastMigrationJob({ authToken, baseUrl, }: getLastMigrationJobProps): Promise { - const url = baseUrl + "/v1/jobs"; + const url = normalizeURLScheme(baseUrl + "/v1/jobs"); const result = await fetch(url, { method: "GET", headers: { From acd83ed81bc109d4c26b0181436009534b0dcc20 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Wed, 29 May 2024 14:58:20 -0400 Subject: [PATCH 44/45] Add a timeout of 2 minutes to GitHub actions --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 90762c82..a9f05ce2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -8,6 +8,7 @@ jobs: "wasm-test": name: "Build and test Wasm on Node.js" runs-on: ubuntu-latest + timeout-minutes: 2 defaults: run: working-directory: ./packages/libsql-client-wasm @@ -35,6 +36,7 @@ jobs: "node-test": name: "Build and test on Node.js" runs-on: ubuntu-latest + timeout-minutes: 2 defaults: run: working-directory: ./packages/libsql-client @@ -95,6 +97,7 @@ jobs: "workers-test": name: "Build and test with Cloudflare Workers" runs-on: ubuntu-latest + timeout-minutes: 2 defaults: run: working-directory: ./packages/libsql-client From dfd6d3f059b104784171c5eebc14238943fc3879 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Thu, 30 May 2024 10:42:55 -0400 Subject: [PATCH 45/45] Disable workers test --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a9f05ce2..1dcae740 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -96,6 +96,7 @@ jobs: "workers-test": name: "Build and test with Cloudflare Workers" + if: false runs-on: ubuntu-latest timeout-minutes: 2 defaults: