@hot-updater/test-utils 1.0.0-rc.17 → 1.0.0-rc.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.cjs CHANGED
@@ -36,6 +36,47 @@ let node_net = require("node:net");
36
36
  node_net = __toESM(node_net, 1);
37
37
  let node_http = require("node:http");
38
38
  let execa = require("execa");
39
+ //#region src/d1TestDatabase.ts
40
+ /**
41
+ * A D1 database over `node:sqlite`, answering as D1 does, after it runs
42
+ * `statements`, such as a provider's schema: what a D1 executor's specs
43
+ * run on without Cloudflare. `db` is a database the spec opens, such as
44
+ * `new DatabaseSync(":memory:")`.
45
+ */
46
+ const createD1TestDatabase = (db, statements = []) => {
47
+ for (const sql of statements) db.exec(sql);
48
+ const run = (sql, params) => {
49
+ const statement = db.prepare(sql);
50
+ const values = params;
51
+ return statement.columns().length > 0 ? {
52
+ success: true,
53
+ results: statement.all(...values),
54
+ meta: {}
55
+ } : {
56
+ success: true,
57
+ results: [],
58
+ meta: { changes: Number(statement.run(...values).changes) }
59
+ };
60
+ };
61
+ /** Runs statements atomically, as `batch` does. */
62
+ const batch = (statements) => {
63
+ db.exec("BEGIN IMMEDIATE");
64
+ try {
65
+ const results = statements.map(({ sql, params }) => run(sql, params));
66
+ db.exec("COMMIT");
67
+ return results;
68
+ } catch (error) {
69
+ db.exec("ROLLBACK");
70
+ throw error;
71
+ }
72
+ };
73
+ return {
74
+ db,
75
+ run,
76
+ batch
77
+ };
78
+ };
79
+ //#endregion
39
80
  //#region ../../node_modules/.pnpm/workspace-tools@0.41.7/node_modules/workspace-tools/lib/graph/getPackageDependencies.js
40
81
  var require_getPackageDependencies = /* @__PURE__ */ __commonJSMin(((exports) => {
41
82
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -20413,6 +20454,167 @@ async function cleanupServer(baseUrl, serverProcess, testDbPath) {
20413
20454
  } catch {}
20414
20455
  }
20415
20456
  //#endregion
20457
+ //#region src/sqlTestExecutors.ts
20458
+ /** Test executors for the SQL core; provider executors arrive with D1–D3. */
20459
+ const pgliteExecutor = (db) => {
20460
+ const over = (runner) => ({ execute: async ({ sql, params }) => {
20461
+ const result = await runner.query(sql, [...params]);
20462
+ return {
20463
+ rows: result.rows,
20464
+ changes: result.affectedRows ?? 0
20465
+ };
20466
+ } });
20467
+ return {
20468
+ dialect: "postgresql",
20469
+ ...over(db),
20470
+ transaction: (fn) => db.transaction((tx) => fn(over(tx)))
20471
+ };
20472
+ };
20473
+ /** One connection, so statements and transactions run one at a time. */
20474
+ const sqliteExecutor = (db) => {
20475
+ let queue = Promise.resolve();
20476
+ const serial = (task) => {
20477
+ const next = queue.then(task);
20478
+ queue = next.catch(() => void 0);
20479
+ return next;
20480
+ };
20481
+ const execute = async ({ sql, params }) => {
20482
+ const statement = db.prepare(sql);
20483
+ const values = params;
20484
+ if (statement.columns().length > 0) return {
20485
+ rows: statement.all(...values),
20486
+ changes: 0
20487
+ };
20488
+ return {
20489
+ rows: [],
20490
+ changes: Number(statement.run(...values).changes)
20491
+ };
20492
+ };
20493
+ return {
20494
+ dialect: "sqlite",
20495
+ execute: (statement) => serial(() => execute(statement)),
20496
+ transaction: (fn) => serial(async () => {
20497
+ db.exec("BEGIN IMMEDIATE");
20498
+ try {
20499
+ const result = await fn({ execute });
20500
+ db.exec("COMMIT");
20501
+ return result;
20502
+ } catch (error) {
20503
+ db.exec("ROLLBACK");
20504
+ throw error;
20505
+ }
20506
+ })
20507
+ };
20508
+ };
20509
+ /**
20510
+ * An executor without interactive transactions, as D1's: writes run as one
20511
+ * atomic batch, here in a SQLite transaction that rolls back on any error.
20512
+ */
20513
+ const sqliteBatchExecutor = (db) => {
20514
+ const executor = sqliteExecutor(db);
20515
+ return {
20516
+ dialect: "sqlite",
20517
+ execute: executor.execute,
20518
+ transaction: () => {
20519
+ throw new Error("A batch executor has no interactive transactions.");
20520
+ },
20521
+ batch: (statements) => executor.transaction(async (connection) => {
20522
+ const results = [];
20523
+ for (const statement of statements) results.push(await connection.execute(statement));
20524
+ return results;
20525
+ })
20526
+ };
20527
+ };
20528
+ /** PGlite without interactive transactions, as an RPC applying a batch would be. */
20529
+ const pgliteBatchExecutor = (db) => {
20530
+ const executor = pgliteExecutor(db);
20531
+ return {
20532
+ dialect: "postgresql",
20533
+ execute: executor.execute,
20534
+ transaction: () => {
20535
+ throw new Error("A batch executor has no interactive transactions.");
20536
+ },
20537
+ batch: (statements) => executor.transaction(async (connection) => {
20538
+ const results = [];
20539
+ for (const statement of statements) results.push(await connection.execute(statement));
20540
+ return results;
20541
+ })
20542
+ };
20543
+ };
20544
+ const pgExecutor = (pool) => {
20545
+ const over = (runner) => ({ execute: async ({ sql, params }) => {
20546
+ const result = await runner.query(sql, [...params]);
20547
+ return {
20548
+ rows: result.rows,
20549
+ changes: result.rowCount ?? 0
20550
+ };
20551
+ } });
20552
+ return {
20553
+ dialect: "postgresql",
20554
+ ...over(pool),
20555
+ async transaction(fn) {
20556
+ const client = await pool.connect();
20557
+ try {
20558
+ await client.query("BEGIN");
20559
+ const result = await fn(over(client));
20560
+ await client.query("COMMIT");
20561
+ return result;
20562
+ } catch (error) {
20563
+ await client.query("ROLLBACK").catch(() => void 0);
20564
+ throw error;
20565
+ } finally {
20566
+ client.release();
20567
+ }
20568
+ }
20569
+ };
20570
+ };
20571
+ /** A pooled PostgreSQL batch: one READ COMMITTED transaction per write, as Supabase's apply RPC runs. */
20572
+ const pgBatchExecutor = (pool) => {
20573
+ const executor = pgExecutor(pool);
20574
+ return {
20575
+ dialect: "postgresql",
20576
+ execute: executor.execute,
20577
+ transaction: () => {
20578
+ throw new Error("A batch executor has no interactive transactions.");
20579
+ },
20580
+ batch: (statements) => executor.transaction(async (connection) => {
20581
+ const results = [];
20582
+ for (const statement of statements) results.push(await connection.execute(statement));
20583
+ return results;
20584
+ })
20585
+ };
20586
+ };
20587
+ const mysqlExecutor = (pool) => {
20588
+ const over = (runner) => ({ execute: async ({ sql, params }) => {
20589
+ const [result] = await runner.query(sql, [...params]);
20590
+ return Array.isArray(result) ? {
20591
+ rows: result,
20592
+ changes: 0
20593
+ } : {
20594
+ rows: [],
20595
+ changes: result.affectedRows
20596
+ };
20597
+ } });
20598
+ return {
20599
+ dialect: "mysql",
20600
+ ...over(pool),
20601
+ async transaction(fn) {
20602
+ const connection = await pool.getConnection();
20603
+ try {
20604
+ await connection.beginTransaction();
20605
+ const result = await fn(over(connection));
20606
+ await connection.commit();
20607
+ return result;
20608
+ } catch (error) {
20609
+ await connection.rollback().catch(() => void 0);
20610
+ throw error;
20611
+ } finally {
20612
+ connection.release();
20613
+ }
20614
+ }
20615
+ };
20616
+ };
20617
+ //#endregion
20416
20618
  exports.RELEASE_CATALOG_DOWNLOAD_HASH = RELEASE_CATALOG_DOWNLOAD_HASH;
20417
20619
  exports.RELEASE_CATALOG_MANIFEST_URIS = RELEASE_CATALOG_MANIFEST_URIS;
20418
20620
  exports.RELEASE_CATALOG_STORAGE_FIXTURES = RELEASE_CATALOG_STORAGE_FIXTURES;
@@ -20422,6 +20624,7 @@ exports.assertDockerComposeAvailable = assertDockerComposeAvailable;
20422
20624
  exports.assertDockerDaemonAvailable = assertDockerDaemonAvailable;
20423
20625
  exports.assertEnvFlagEnabled = assertEnvFlagEnabled;
20424
20626
  exports.cleanupServer = cleanupServer;
20627
+ exports.createD1TestDatabase = createD1TestDatabase;
20425
20628
  exports.createReleaseCatalogTestStorage = createReleaseCatalogTestStorage;
20426
20629
  exports.createTestDbPath = createTestDbPath;
20427
20630
  exports.findOpenPort = findOpenPort;
@@ -20431,11 +20634,18 @@ exports.hasDockerCompose = hasDockerCompose;
20431
20634
  exports.hasDockerDaemon = hasDockerDaemon;
20432
20635
  exports.killPort = killPort;
20433
20636
  exports.mockReactNativeProjectRoot = mockReactNativeProjectRoot;
20637
+ exports.mysqlExecutor = mysqlExecutor;
20638
+ exports.pgBatchExecutor = pgBatchExecutor;
20639
+ exports.pgExecutor = pgExecutor;
20640
+ exports.pgliteBatchExecutor = pgliteBatchExecutor;
20641
+ exports.pgliteExecutor = pgliteExecutor;
20434
20642
  exports.releaseCatalogArtifact = releaseCatalogArtifact;
20435
20643
  exports.releaseCatalogDownloadUrl = releaseCatalogDownloadUrl;
20436
20644
  exports.runCheckedCommand = runCheckedCommand;
20437
20645
  exports.spawnRuntime = spawnRuntime;
20438
20646
  exports.spawnServerProcess = spawnServerProcess;
20647
+ exports.sqliteBatchExecutor = sqliteBatchExecutor;
20648
+ exports.sqliteExecutor = sqliteExecutor;
20439
20649
  exports.startHttpTestServer = startHttpTestServer;
20440
20650
  exports.stopRuntime = stopRuntime;
20441
20651
  exports.waitForHttpOk = waitForHttpOk;
package/dist/node.d.cts CHANGED
@@ -1,8 +1,36 @@
1
+ import { DatabaseSync } from "node:sqlite";
1
2
  import { ChildProcessByStdio } from "node:child_process";
2
3
  import { Readable } from "node:stream";
3
- import { ArtifactInfo } from "@hot-updater/core";
4
- import { BundleRow, StoragePlugin } from "@hot-updater/plugin-core";
4
+ import { BundleRow, SqlExecutor, StorageAdapter } from "@hot-updater/plugin-core";
5
+ import { ArtifactInfo } from "@hot-updater/protocol";
5
6
  import { execa } from "execa";
7
+ import { PGlite } from "@electric-sql/pglite";
8
+ import { Pool } from "mysql2/promise";
9
+ import { Pool as Pool$1 } from "pg";
10
+ //#region src/d1TestDatabase.d.ts
11
+ /** What a D1 statement answers: `results` for a read, `meta.changes` for a write. */
12
+ interface D1TestResult {
13
+ readonly success: true;
14
+ readonly results: readonly unknown[];
15
+ readonly meta: {
16
+ readonly changes?: number;
17
+ };
18
+ }
19
+ /**
20
+ * A D1 database over `node:sqlite`, answering as D1 does, after it runs
21
+ * `statements`, such as a provider's schema: what a D1 executor's specs
22
+ * run on without Cloudflare. `db` is a database the spec opens, such as
23
+ * `new DatabaseSync(":memory:")`.
24
+ */
25
+ declare const createD1TestDatabase: (db: DatabaseSync, statements?: readonly string[]) => {
26
+ db: DatabaseSync;
27
+ run: (sql: string, params: readonly unknown[]) => D1TestResult;
28
+ batch: (statements: readonly {
29
+ sql: string;
30
+ params: readonly unknown[];
31
+ }[]) => D1TestResult[];
32
+ };
33
+ //#endregion
6
34
  //#region src/mockReactNativeProjectRoot.d.ts
7
35
  interface MockedReactNativeProjectRoot {
8
36
  rootDir: string;
@@ -73,7 +101,7 @@ declare const releaseCatalogArtifact: (bundle: BundleRow) => ArtifactInfo;
73
101
  /** Install these storage objects in the test server, independently of its DB. */
74
102
  declare const RELEASE_CATALOG_STORAGE_FIXTURES: Readonly<Record<string, string>>;
75
103
  /** Storage boundary shared by provider and separately spawned server tests. */
76
- declare const createReleaseCatalogTestStorage: () => StoragePlugin;
104
+ declare const createReleaseCatalogTestStorage: () => StorageAdapter;
77
105
  //#endregion
78
106
  //#region src/setupHandlerIntegrationTestSuite.d.ts
79
107
  declare const TEST_ADMIN_AUTH_TOKEN = "hot-updater-test-token";
@@ -104,4 +132,21 @@ declare function spawnServerProcess(options: {
104
132
  */
105
133
  declare function cleanupServer(baseUrl: string, serverProcess: ReturnType<typeof execa> | null, testDbPath: string): Promise<void>;
106
134
  //#endregion
107
- export { MockedReactNativeProjectRoot, RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, RuntimeChild, RuntimeLogs, SpawnRuntimeOptions, TEST_ADMIN_AUTH_TOKEN, assertCommandAvailable, assertDockerComposeAvailable, assertDockerDaemonAvailable, assertEnvFlagEnabled, cleanupServer, createReleaseCatalogTestStorage, createTestDbPath, findOpenPort, formatRuntimeLogs, hasCommand, hasDockerCompose, hasDockerDaemon, killPort, mockReactNativeProjectRoot, releaseCatalogArtifact, releaseCatalogDownloadUrl, runCheckedCommand, spawnRuntime, spawnServerProcess, startHttpTestServer, stopRuntime, waitForHttpOk, waitForServer };
135
+ //#region src/sqlTestExecutors.d.ts
136
+ /** Test executors for the SQL core; provider executors arrive with D1–D3. */
137
+ declare const pgliteExecutor: (db: PGlite) => SqlExecutor;
138
+ /** One connection, so statements and transactions run one at a time. */
139
+ declare const sqliteExecutor: (db: DatabaseSync) => SqlExecutor;
140
+ /**
141
+ * An executor without interactive transactions, as D1's: writes run as one
142
+ * atomic batch, here in a SQLite transaction that rolls back on any error.
143
+ */
144
+ declare const sqliteBatchExecutor: (db: DatabaseSync) => SqlExecutor;
145
+ /** PGlite without interactive transactions, as an RPC applying a batch would be. */
146
+ declare const pgliteBatchExecutor: (db: PGlite) => SqlExecutor;
147
+ declare const pgExecutor: (pool: Pool$1) => SqlExecutor;
148
+ /** A pooled PostgreSQL batch: one READ COMMITTED transaction per write, as Supabase's apply RPC runs. */
149
+ declare const pgBatchExecutor: (pool: Pool$1) => SqlExecutor;
150
+ declare const mysqlExecutor: (pool: Pool) => SqlExecutor;
151
+ //#endregion
152
+ export { D1TestResult, MockedReactNativeProjectRoot, RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, RuntimeChild, RuntimeLogs, SpawnRuntimeOptions, TEST_ADMIN_AUTH_TOKEN, assertCommandAvailable, assertDockerComposeAvailable, assertDockerDaemonAvailable, assertEnvFlagEnabled, cleanupServer, createD1TestDatabase, createReleaseCatalogTestStorage, createTestDbPath, findOpenPort, formatRuntimeLogs, hasCommand, hasDockerCompose, hasDockerDaemon, killPort, mockReactNativeProjectRoot, mysqlExecutor, pgBatchExecutor, pgExecutor, pgliteBatchExecutor, pgliteExecutor, releaseCatalogArtifact, releaseCatalogDownloadUrl, runCheckedCommand, spawnRuntime, spawnServerProcess, sqliteBatchExecutor, sqliteExecutor, startHttpTestServer, stopRuntime, waitForHttpOk, waitForServer };
package/dist/node.d.mts CHANGED
@@ -1,8 +1,36 @@
1
1
  import { ChildProcessByStdio } from "node:child_process";
2
2
  import { execa } from "execa";
3
+ import { DatabaseSync } from "node:sqlite";
3
4
  import { Readable } from "node:stream";
4
- import { ArtifactInfo } from "@hot-updater/core";
5
- import { BundleRow, StoragePlugin } from "@hot-updater/plugin-core";
5
+ import { BundleRow, SqlExecutor, StorageAdapter } from "@hot-updater/plugin-core";
6
+ import { ArtifactInfo } from "@hot-updater/protocol";
7
+ import { PGlite } from "@electric-sql/pglite";
8
+ import { Pool } from "mysql2/promise";
9
+ import { Pool as Pool$1 } from "pg";
10
+ //#region src/d1TestDatabase.d.ts
11
+ /** What a D1 statement answers: `results` for a read, `meta.changes` for a write. */
12
+ interface D1TestResult {
13
+ readonly success: true;
14
+ readonly results: readonly unknown[];
15
+ readonly meta: {
16
+ readonly changes?: number;
17
+ };
18
+ }
19
+ /**
20
+ * A D1 database over `node:sqlite`, answering as D1 does, after it runs
21
+ * `statements`, such as a provider's schema: what a D1 executor's specs
22
+ * run on without Cloudflare. `db` is a database the spec opens, such as
23
+ * `new DatabaseSync(":memory:")`.
24
+ */
25
+ declare const createD1TestDatabase: (db: DatabaseSync, statements?: readonly string[]) => {
26
+ db: DatabaseSync;
27
+ run: (sql: string, params: readonly unknown[]) => D1TestResult;
28
+ batch: (statements: readonly {
29
+ sql: string;
30
+ params: readonly unknown[];
31
+ }[]) => D1TestResult[];
32
+ };
33
+ //#endregion
6
34
  //#region src/mockReactNativeProjectRoot.d.ts
7
35
  interface MockedReactNativeProjectRoot {
8
36
  rootDir: string;
@@ -73,7 +101,7 @@ declare const releaseCatalogArtifact: (bundle: BundleRow) => ArtifactInfo;
73
101
  /** Install these storage objects in the test server, independently of its DB. */
74
102
  declare const RELEASE_CATALOG_STORAGE_FIXTURES: Readonly<Record<string, string>>;
75
103
  /** Storage boundary shared by provider and separately spawned server tests. */
76
- declare const createReleaseCatalogTestStorage: () => StoragePlugin;
104
+ declare const createReleaseCatalogTestStorage: () => StorageAdapter;
77
105
  //#endregion
78
106
  //#region src/setupHandlerIntegrationTestSuite.d.ts
79
107
  declare const TEST_ADMIN_AUTH_TOKEN = "hot-updater-test-token";
@@ -104,4 +132,21 @@ declare function spawnServerProcess(options: {
104
132
  */
105
133
  declare function cleanupServer(baseUrl: string, serverProcess: ReturnType<typeof execa> | null, testDbPath: string): Promise<void>;
106
134
  //#endregion
107
- export { MockedReactNativeProjectRoot, RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, RuntimeChild, RuntimeLogs, SpawnRuntimeOptions, TEST_ADMIN_AUTH_TOKEN, assertCommandAvailable, assertDockerComposeAvailable, assertDockerDaemonAvailable, assertEnvFlagEnabled, cleanupServer, createReleaseCatalogTestStorage, createTestDbPath, findOpenPort, formatRuntimeLogs, hasCommand, hasDockerCompose, hasDockerDaemon, killPort, mockReactNativeProjectRoot, releaseCatalogArtifact, releaseCatalogDownloadUrl, runCheckedCommand, spawnRuntime, spawnServerProcess, startHttpTestServer, stopRuntime, waitForHttpOk, waitForServer };
135
+ //#region src/sqlTestExecutors.d.ts
136
+ /** Test executors for the SQL core; provider executors arrive with D1–D3. */
137
+ declare const pgliteExecutor: (db: PGlite) => SqlExecutor;
138
+ /** One connection, so statements and transactions run one at a time. */
139
+ declare const sqliteExecutor: (db: DatabaseSync) => SqlExecutor;
140
+ /**
141
+ * An executor without interactive transactions, as D1's: writes run as one
142
+ * atomic batch, here in a SQLite transaction that rolls back on any error.
143
+ */
144
+ declare const sqliteBatchExecutor: (db: DatabaseSync) => SqlExecutor;
145
+ /** PGlite without interactive transactions, as an RPC applying a batch would be. */
146
+ declare const pgliteBatchExecutor: (db: PGlite) => SqlExecutor;
147
+ declare const pgExecutor: (pool: Pool$1) => SqlExecutor;
148
+ /** A pooled PostgreSQL batch: one READ COMMITTED transaction per write, as Supabase's apply RPC runs. */
149
+ declare const pgBatchExecutor: (pool: Pool$1) => SqlExecutor;
150
+ declare const mysqlExecutor: (pool: Pool) => SqlExecutor;
151
+ //#endregion
152
+ export { D1TestResult, MockedReactNativeProjectRoot, RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, RuntimeChild, RuntimeLogs, SpawnRuntimeOptions, TEST_ADMIN_AUTH_TOKEN, assertCommandAvailable, assertDockerComposeAvailable, assertDockerDaemonAvailable, assertEnvFlagEnabled, cleanupServer, createD1TestDatabase, createReleaseCatalogTestStorage, createTestDbPath, findOpenPort, formatRuntimeLogs, hasCommand, hasDockerCompose, hasDockerDaemon, killPort, mockReactNativeProjectRoot, mysqlExecutor, pgBatchExecutor, pgExecutor, pgliteBatchExecutor, pgliteExecutor, releaseCatalogArtifact, releaseCatalogDownloadUrl, runCheckedCommand, spawnRuntime, spawnServerProcess, sqliteBatchExecutor, sqliteExecutor, startHttpTestServer, stopRuntime, waitForHttpOk, waitForServer };
package/dist/node.mjs CHANGED
@@ -12,6 +12,47 @@ import { execa } from "execa";
12
12
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
13
13
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
14
14
  //#endregion
15
+ //#region src/d1TestDatabase.ts
16
+ /**
17
+ * A D1 database over `node:sqlite`, answering as D1 does, after it runs
18
+ * `statements`, such as a provider's schema: what a D1 executor's specs
19
+ * run on without Cloudflare. `db` is a database the spec opens, such as
20
+ * `new DatabaseSync(":memory:")`.
21
+ */
22
+ const createD1TestDatabase = (db, statements = []) => {
23
+ for (const sql of statements) db.exec(sql);
24
+ const run = (sql, params) => {
25
+ const statement = db.prepare(sql);
26
+ const values = params;
27
+ return statement.columns().length > 0 ? {
28
+ success: true,
29
+ results: statement.all(...values),
30
+ meta: {}
31
+ } : {
32
+ success: true,
33
+ results: [],
34
+ meta: { changes: Number(statement.run(...values).changes) }
35
+ };
36
+ };
37
+ /** Runs statements atomically, as `batch` does. */
38
+ const batch = (statements) => {
39
+ db.exec("BEGIN IMMEDIATE");
40
+ try {
41
+ const results = statements.map(({ sql, params }) => run(sql, params));
42
+ db.exec("COMMIT");
43
+ return results;
44
+ } catch (error) {
45
+ db.exec("ROLLBACK");
46
+ throw error;
47
+ }
48
+ };
49
+ return {
50
+ db,
51
+ run,
52
+ batch
53
+ };
54
+ };
55
+ //#endregion
15
56
  //#region ../../node_modules/.pnpm/workspace-tools@0.41.7/node_modules/workspace-tools/lib/graph/getPackageDependencies.js
16
57
  var require_getPackageDependencies = /* @__PURE__ */ __commonJSMin(((exports) => {
17
58
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -20389,4 +20430,165 @@ async function cleanupServer(baseUrl, serverProcess, testDbPath) {
20389
20430
  } catch {}
20390
20431
  }
20391
20432
  //#endregion
20392
- export { RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, TEST_ADMIN_AUTH_TOKEN, assertCommandAvailable, assertDockerComposeAvailable, assertDockerDaemonAvailable, assertEnvFlagEnabled, cleanupServer, createReleaseCatalogTestStorage, createTestDbPath, findOpenPort, formatRuntimeLogs, hasCommand, hasDockerCompose, hasDockerDaemon, killPort, mockReactNativeProjectRoot, releaseCatalogArtifact, releaseCatalogDownloadUrl, runCheckedCommand, spawnRuntime, spawnServerProcess, startHttpTestServer, stopRuntime, waitForHttpOk, waitForServer };
20433
+ //#region src/sqlTestExecutors.ts
20434
+ /** Test executors for the SQL core; provider executors arrive with D1–D3. */
20435
+ const pgliteExecutor = (db) => {
20436
+ const over = (runner) => ({ execute: async ({ sql, params }) => {
20437
+ const result = await runner.query(sql, [...params]);
20438
+ return {
20439
+ rows: result.rows,
20440
+ changes: result.affectedRows ?? 0
20441
+ };
20442
+ } });
20443
+ return {
20444
+ dialect: "postgresql",
20445
+ ...over(db),
20446
+ transaction: (fn) => db.transaction((tx) => fn(over(tx)))
20447
+ };
20448
+ };
20449
+ /** One connection, so statements and transactions run one at a time. */
20450
+ const sqliteExecutor = (db) => {
20451
+ let queue = Promise.resolve();
20452
+ const serial = (task) => {
20453
+ const next = queue.then(task);
20454
+ queue = next.catch(() => void 0);
20455
+ return next;
20456
+ };
20457
+ const execute = async ({ sql, params }) => {
20458
+ const statement = db.prepare(sql);
20459
+ const values = params;
20460
+ if (statement.columns().length > 0) return {
20461
+ rows: statement.all(...values),
20462
+ changes: 0
20463
+ };
20464
+ return {
20465
+ rows: [],
20466
+ changes: Number(statement.run(...values).changes)
20467
+ };
20468
+ };
20469
+ return {
20470
+ dialect: "sqlite",
20471
+ execute: (statement) => serial(() => execute(statement)),
20472
+ transaction: (fn) => serial(async () => {
20473
+ db.exec("BEGIN IMMEDIATE");
20474
+ try {
20475
+ const result = await fn({ execute });
20476
+ db.exec("COMMIT");
20477
+ return result;
20478
+ } catch (error) {
20479
+ db.exec("ROLLBACK");
20480
+ throw error;
20481
+ }
20482
+ })
20483
+ };
20484
+ };
20485
+ /**
20486
+ * An executor without interactive transactions, as D1's: writes run as one
20487
+ * atomic batch, here in a SQLite transaction that rolls back on any error.
20488
+ */
20489
+ const sqliteBatchExecutor = (db) => {
20490
+ const executor = sqliteExecutor(db);
20491
+ return {
20492
+ dialect: "sqlite",
20493
+ execute: executor.execute,
20494
+ transaction: () => {
20495
+ throw new Error("A batch executor has no interactive transactions.");
20496
+ },
20497
+ batch: (statements) => executor.transaction(async (connection) => {
20498
+ const results = [];
20499
+ for (const statement of statements) results.push(await connection.execute(statement));
20500
+ return results;
20501
+ })
20502
+ };
20503
+ };
20504
+ /** PGlite without interactive transactions, as an RPC applying a batch would be. */
20505
+ const pgliteBatchExecutor = (db) => {
20506
+ const executor = pgliteExecutor(db);
20507
+ return {
20508
+ dialect: "postgresql",
20509
+ execute: executor.execute,
20510
+ transaction: () => {
20511
+ throw new Error("A batch executor has no interactive transactions.");
20512
+ },
20513
+ batch: (statements) => executor.transaction(async (connection) => {
20514
+ const results = [];
20515
+ for (const statement of statements) results.push(await connection.execute(statement));
20516
+ return results;
20517
+ })
20518
+ };
20519
+ };
20520
+ const pgExecutor = (pool) => {
20521
+ const over = (runner) => ({ execute: async ({ sql, params }) => {
20522
+ const result = await runner.query(sql, [...params]);
20523
+ return {
20524
+ rows: result.rows,
20525
+ changes: result.rowCount ?? 0
20526
+ };
20527
+ } });
20528
+ return {
20529
+ dialect: "postgresql",
20530
+ ...over(pool),
20531
+ async transaction(fn) {
20532
+ const client = await pool.connect();
20533
+ try {
20534
+ await client.query("BEGIN");
20535
+ const result = await fn(over(client));
20536
+ await client.query("COMMIT");
20537
+ return result;
20538
+ } catch (error) {
20539
+ await client.query("ROLLBACK").catch(() => void 0);
20540
+ throw error;
20541
+ } finally {
20542
+ client.release();
20543
+ }
20544
+ }
20545
+ };
20546
+ };
20547
+ /** A pooled PostgreSQL batch: one READ COMMITTED transaction per write, as Supabase's apply RPC runs. */
20548
+ const pgBatchExecutor = (pool) => {
20549
+ const executor = pgExecutor(pool);
20550
+ return {
20551
+ dialect: "postgresql",
20552
+ execute: executor.execute,
20553
+ transaction: () => {
20554
+ throw new Error("A batch executor has no interactive transactions.");
20555
+ },
20556
+ batch: (statements) => executor.transaction(async (connection) => {
20557
+ const results = [];
20558
+ for (const statement of statements) results.push(await connection.execute(statement));
20559
+ return results;
20560
+ })
20561
+ };
20562
+ };
20563
+ const mysqlExecutor = (pool) => {
20564
+ const over = (runner) => ({ execute: async ({ sql, params }) => {
20565
+ const [result] = await runner.query(sql, [...params]);
20566
+ return Array.isArray(result) ? {
20567
+ rows: result,
20568
+ changes: 0
20569
+ } : {
20570
+ rows: [],
20571
+ changes: result.affectedRows
20572
+ };
20573
+ } });
20574
+ return {
20575
+ dialect: "mysql",
20576
+ ...over(pool),
20577
+ async transaction(fn) {
20578
+ const connection = await pool.getConnection();
20579
+ try {
20580
+ await connection.beginTransaction();
20581
+ const result = await fn(over(connection));
20582
+ await connection.commit();
20583
+ return result;
20584
+ } catch (error) {
20585
+ await connection.rollback().catch(() => void 0);
20586
+ throw error;
20587
+ } finally {
20588
+ connection.release();
20589
+ }
20590
+ }
20591
+ };
20592
+ };
20593
+ //#endregion
20594
+ export { RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, TEST_ADMIN_AUTH_TOKEN, assertCommandAvailable, assertDockerComposeAvailable, assertDockerDaemonAvailable, assertEnvFlagEnabled, cleanupServer, createD1TestDatabase, createReleaseCatalogTestStorage, createTestDbPath, findOpenPort, formatRuntimeLogs, hasCommand, hasDockerCompose, hasDockerDaemon, killPort, mockReactNativeProjectRoot, mysqlExecutor, pgBatchExecutor, pgExecutor, pgliteBatchExecutor, pgliteExecutor, releaseCatalogArtifact, releaseCatalogDownloadUrl, runCheckedCommand, spawnRuntime, spawnServerProcess, sqliteBatchExecutor, sqliteExecutor, startHttpTestServer, stopRuntime, waitForHttpOk, waitForServer };