orez 0.2.26 → 0.2.29
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/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +9 -1
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/pg-proxy-do-backend.d.ts +2 -0
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +49 -7
- package/dist/pg-proxy-do-backend.js.map +1 -1
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
- package/dist/pg-sqlite-compiler/index.d.ts +12 -0
- package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/index.js +59 -0
- package/dist/pg-sqlite-compiler/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.js +39 -0
- package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.js +103 -0
- package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
- package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
- package/dist/pg-sqlite-compiler/types.d.ts +55 -0
- package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/types.js +2 -0
- package/dist/pg-sqlite-compiler/types.js.map +1 -0
- package/package.json +8 -4
- package/src/admin/admin-data.test.ts +0 -348
- package/src/admin/http-proxy.ts +0 -252
- package/src/admin/log-store.ts +0 -192
- package/src/admin/server.ts +0 -471
- package/src/admin/ui.ts +0 -1322
- package/src/bench/proxy-throughput.bench.ts +0 -343
- package/src/bench/serial-mutations.bench.ts +0 -270
- package/src/browser.ts +0 -203
- package/src/cf-do/.wrangler/cache/cf.json +0 -1
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
- package/src/cf-do/ARCHITECTURE.md +0 -83
- package/src/cf-do/watermark.test.ts +0 -103
- package/src/cf-do/watermark.ts +0 -118
- package/src/cf-do/worker.ts +0 -1033
- package/src/cf-do/wrangler.toml +0 -11
- package/src/cf-pglite/README.md +0 -19
- package/src/change-tracking.ts +0 -25
- package/src/child-process.test.ts +0 -147
- package/src/child-process.ts +0 -90
- package/src/cli-entry.ts +0 -72
- package/src/cli.test.ts +0 -38
- package/src/cli.ts +0 -1214
- package/src/config.ts +0 -150
- package/src/do-sql-tracking.test.ts +0 -19
- package/src/do-sql-tracking.ts +0 -19
- package/src/index.ts +0 -1215
- package/src/integration/integration.test.ts +0 -517
- package/src/integration/native-binary.guard.test.ts +0 -13
- package/src/integration/native-startup.test.ts +0 -44
- package/src/integration/replication-latency.test.ts +0 -428
- package/src/integration/restore-live-stress.test.ts +0 -433
- package/src/integration/restore-reset.test.ts +0 -400
- package/src/integration/restore.test.ts +0 -274
- package/src/integration/test-permissions.ts +0 -147
- package/src/load-config.ts +0 -46
- package/src/log.ts +0 -96
- package/src/mutex.ts +0 -47
- package/src/pg-proxy-browser.singledb.test.ts +0 -233
- package/src/pg-proxy-browser.ts +0 -2022
- package/src/pg-proxy-do-backend.test.ts +0 -3890
- package/src/pg-proxy-do-backend.ts +0 -7157
- package/src/pg-proxy.ts +0 -1087
- package/src/pglite-ipc.test.ts +0 -116
- package/src/pglite-ipc.ts +0 -266
- package/src/pglite-manager.ts +0 -557
- package/src/pglite-web-proxy.test.ts +0 -57
- package/src/pglite-web-proxy.ts +0 -221
- package/src/pglite-web-worker.ts +0 -152
- package/src/pglite-worker-thread.ts +0 -253
- package/src/port.ts +0 -25
- package/src/process-title.ts +0 -9
- package/src/recovery.ts +0 -155
- package/src/replication/change-tracker.test.ts +0 -357
- package/src/replication/change-tracker.ts +0 -279
- package/src/replication/handler.test.ts +0 -511
- package/src/replication/handler.ts +0 -1190
- package/src/replication/pgoutput-encoder.test.ts +0 -697
- package/src/replication/pgoutput-encoder.ts +0 -373
- package/src/replication/tcp-replication.test.ts +0 -876
- package/src/replication/zero-compat.test.ts +0 -1150
- package/src/restore-stress.test.ts +0 -188
- package/src/s3-local.ts +0 -203
- package/src/shim/hooks.mjs +0 -120
- package/src/shim/register.mjs +0 -4
- package/src/sqlite-mode/apply-mode.ts +0 -224
- package/src/sqlite-mode/index.ts +0 -15
- package/src/sqlite-mode/native-binary.ts +0 -89
- package/src/sqlite-mode/package-resolve.ts +0 -17
- package/src/sqlite-mode/resolve-mode.ts +0 -80
- package/src/sqlite-mode/shim-template.ts +0 -159
- package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
- package/src/sqlite-mode/types.ts +0 -30
- package/src/vite-plugin.ts +0 -67
- package/src/wasm-sqlite.test.ts +0 -537
- package/src/worker/browser-admin.ts +0 -52
- package/src/worker/browser-build-config.test.ts +0 -71
- package/src/worker/browser-build-config.ts +0 -109
- package/src/worker/browser-embed-admin.test.ts +0 -75
- package/src/worker/browser-embed.ts +0 -345
- package/src/worker/cf-patches.ts +0 -384
- package/src/worker/embed-integration.test.ts +0 -321
- package/src/worker/index.ts +0 -138
- package/src/worker/shims/fastify.test.ts +0 -255
- package/src/worker/shims/fastify.ts +0 -306
- package/src/worker/shims/http-service.test.ts +0 -355
- package/src/worker/shims/http-service.ts +0 -293
- package/src/worker/shims/node-stub.ts +0 -290
- package/src/worker/shims/oxfmt.ts +0 -3
- package/src/worker/shims/postgres-browser.ts +0 -59
- package/src/worker/shims/postgres-socket.test.ts +0 -576
- package/src/worker/shims/postgres-socket.ts +0 -310
- package/src/worker/shims/postgres.test.ts +0 -364
- package/src/worker/shims/postgres.ts +0 -1454
- package/src/worker/shims/sqlite-browser.test.ts +0 -233
- package/src/worker/shims/sqlite-browser.ts +0 -175
- package/src/worker/shims/sqlite.test.ts +0 -786
- package/src/worker/shims/sqlite.ts +0 -978
- package/src/worker/shims/stream-browser.ts +0 -15
- package/src/worker/shims/ws-browser.test.ts +0 -205
- package/src/worker/shims/ws-browser.ts +0 -248
- package/src/worker/shims/ws.test.ts +0 -288
- package/src/worker/shims/ws.ts +0 -467
- package/src/worker/shims/zero-process-env.ts +0 -11
- package/src/worker/types.ts +0 -75
- package/src/worker/worker-integration.test.ts +0 -223
- package/src/worker/worker.test.ts +0 -136
- package/src/worker/zero-cache-embed-cf.ts +0 -463
- package/src/worker/zero-cache-embed.ts +0 -277
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pgsqlite-backed oracle for compiler quality.
|
|
3
|
+
*
|
|
4
|
+
* Spawns a real pgsqlite server, sends PG SQL via the PG wire protocol,
|
|
5
|
+
* captures the result set. Compares against running the same query through
|
|
6
|
+
* our compiler + bun:sqlite. Equivalence at the result-set level is what we
|
|
7
|
+
* actually care about — pgsqlite is the oracle, not the spec.
|
|
8
|
+
*
|
|
9
|
+
* The pgsqlite binary path comes from `vendor/pgsqlite/.resolved-path`,
|
|
10
|
+
* populated by `scripts/pgsqlite/ensure.ts`. If empty, oracle tests should
|
|
11
|
+
* be marked `it.skip` so the suite still runs without pgsqlite installed.
|
|
12
|
+
*/
|
|
13
|
+
import { spawn } from 'node:child_process';
|
|
14
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
15
|
+
import { createConnection, createServer } from 'node:net';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
import { resolve } from 'node:path';
|
|
18
|
+
import Database from '@rocicorp/zero-sqlite3';
|
|
19
|
+
const VENDOR_PATH_FILE = resolve(import.meta.dirname, '..', '..', '..', 'vendor', 'pgsqlite', '.resolved-path');
|
|
20
|
+
export function pgsqliteBinPath() {
|
|
21
|
+
if (!existsSync(VENDOR_PATH_FILE))
|
|
22
|
+
return null;
|
|
23
|
+
const path = readFileSync(VENDOR_PATH_FILE, 'utf-8').trim();
|
|
24
|
+
return path && existsSync(path) ? path : null;
|
|
25
|
+
}
|
|
26
|
+
export const ORACLE_AVAILABLE = pgsqliteBinPath() !== null;
|
|
27
|
+
/**
|
|
28
|
+
* Pick a free TCP port by binding ephemeral and reading what we got.
|
|
29
|
+
* Then close immediately and hand the port to pgsqlite. There's a tiny TOCTOU
|
|
30
|
+
* window where another process could grab it before pgsqlite binds, but
|
|
31
|
+
* vitest's parallel test files all go through this helper, so the only races
|
|
32
|
+
* are against unrelated processes on the host — vanishingly unlikely in CI.
|
|
33
|
+
*/
|
|
34
|
+
async function getFreePort() {
|
|
35
|
+
return new Promise((resolveFn, reject) => {
|
|
36
|
+
const server = createServer();
|
|
37
|
+
server.unref();
|
|
38
|
+
server.on('error', reject);
|
|
39
|
+
server.listen(0, '127.0.0.1', () => {
|
|
40
|
+
const addr = server.address();
|
|
41
|
+
if (!addr || typeof addr === 'string') {
|
|
42
|
+
server.close();
|
|
43
|
+
reject(new Error('failed to get free port'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const port = addr.port;
|
|
47
|
+
server.close(() => resolveFn(port));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function probePort(port, timeoutMs) {
|
|
52
|
+
return new Promise((resolveFn) => {
|
|
53
|
+
const sock = createConnection({ host: '127.0.0.1', port });
|
|
54
|
+
const cleanup = (val) => {
|
|
55
|
+
try {
|
|
56
|
+
sock.destroy();
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
resolveFn(val);
|
|
60
|
+
};
|
|
61
|
+
const timer = setTimeout(() => cleanup(false), timeoutMs);
|
|
62
|
+
sock.once('connect', () => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
cleanup(true);
|
|
65
|
+
});
|
|
66
|
+
sock.once('error', () => {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
cleanup(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Start a pgsqlite server on an OS-assigned ephemeral port with an ephemeral
|
|
74
|
+
* database. Throws on probe timeout. Caller is responsible for calling
|
|
75
|
+
* `stop()` in a `finally` / `afterAll` hook — `stop()` awaits child exit
|
|
76
|
+
* and cleans up the tempdir.
|
|
77
|
+
*/
|
|
78
|
+
export async function startPgsqliteServer() {
|
|
79
|
+
const bin = pgsqliteBinPath();
|
|
80
|
+
if (!bin)
|
|
81
|
+
throw new Error('pgsqlite binary not available');
|
|
82
|
+
const port = await getFreePort();
|
|
83
|
+
const tmpDir = mkdtempSync(resolve(tmpdir(), 'orez-oracle-'));
|
|
84
|
+
const dbPath = resolve(tmpDir, 'pg.db');
|
|
85
|
+
const proc = spawn(bin, ['--port', String(port), '--database', dbPath, '--log-level', 'error'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
86
|
+
const exited = new Promise((res) => proc.once('exit', () => res()));
|
|
87
|
+
// wait for "ready" — poll until the server accepts a connection or we time out
|
|
88
|
+
let ready = false;
|
|
89
|
+
const start = Date.now();
|
|
90
|
+
while (Date.now() - start < 10_000) {
|
|
91
|
+
if (await probePort(port, 250)) {
|
|
92
|
+
ready = true;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
if (proc.exitCode !== null) {
|
|
96
|
+
throw new Error(`pgsqlite exited before becoming ready (code=${proc.exitCode})`);
|
|
97
|
+
}
|
|
98
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
99
|
+
}
|
|
100
|
+
if (!ready) {
|
|
101
|
+
try {
|
|
102
|
+
proc.kill('SIGKILL');
|
|
103
|
+
}
|
|
104
|
+
catch { }
|
|
105
|
+
try {
|
|
106
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
throw new Error(`pgsqlite did not become ready on port ${port} within 10s`);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
port,
|
|
113
|
+
dbPath,
|
|
114
|
+
async stop() {
|
|
115
|
+
try {
|
|
116
|
+
proc.kill('SIGTERM');
|
|
117
|
+
}
|
|
118
|
+
catch { }
|
|
119
|
+
// wait up to 2s for graceful exit, then SIGKILL
|
|
120
|
+
const killTimer = setTimeout(() => {
|
|
121
|
+
try {
|
|
122
|
+
proc.kill('SIGKILL');
|
|
123
|
+
}
|
|
124
|
+
catch { }
|
|
125
|
+
}, 2_000);
|
|
126
|
+
try {
|
|
127
|
+
await exited;
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
clearTimeout(killTimer);
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
134
|
+
}
|
|
135
|
+
catch { }
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Helper: open a postgres.js client to a running pgsqlite server.
|
|
141
|
+
* Requires `postgres` to be available (we already ship it via @rocicorp/zero).
|
|
142
|
+
*/
|
|
143
|
+
export async function connectOracle(server) {
|
|
144
|
+
const { default: postgres } = await import('postgres');
|
|
145
|
+
const sql = postgres({
|
|
146
|
+
host: '127.0.0.1',
|
|
147
|
+
port: server.port,
|
|
148
|
+
user: 'oracle',
|
|
149
|
+
password: '',
|
|
150
|
+
database: 'main',
|
|
151
|
+
ssl: false,
|
|
152
|
+
max: 1,
|
|
153
|
+
idle_timeout: 5,
|
|
154
|
+
connect_timeout: 5,
|
|
155
|
+
fetch_types: false,
|
|
156
|
+
prepare: false,
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
exec: async (s, params = []) => {
|
|
160
|
+
const rows = await sql.unsafe(s, params);
|
|
161
|
+
return rows;
|
|
162
|
+
},
|
|
163
|
+
end: () => sql.end({ timeout: 2 }).then(() => undefined),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Run the same query against pgsqlite (oracle) and our compiler+SQLite (under
|
|
168
|
+
* test). Return both result sets so the test can diff them however it wants.
|
|
169
|
+
*
|
|
170
|
+
* Uses freshly-spawned servers/dbs for each call — slow but isolated. For
|
|
171
|
+
* batches use the lower-level helpers above.
|
|
172
|
+
*/
|
|
173
|
+
export async function runOracleAndCompiler(setupSql, pgSql, params = []) {
|
|
174
|
+
const server = await startPgsqliteServer();
|
|
175
|
+
try {
|
|
176
|
+
const conn = await connectOracle(server);
|
|
177
|
+
try {
|
|
178
|
+
for (const s of setupSql)
|
|
179
|
+
await conn.exec(s);
|
|
180
|
+
const oracle = await conn.exec(pgSql, params);
|
|
181
|
+
// ours: setup + query against fresh in-memory sqlite (after compile())
|
|
182
|
+
const { compile } = await import('../index.js');
|
|
183
|
+
const db = new Database(':memory:');
|
|
184
|
+
for (const s of setupSql) {
|
|
185
|
+
const { sql: translated } = compile(s);
|
|
186
|
+
db.exec(translated);
|
|
187
|
+
}
|
|
188
|
+
const { sql: translatedQuery } = compile(pgSql);
|
|
189
|
+
// postgres.js uses $1 params; sqlite uses ?
|
|
190
|
+
const sqliteSql = translatedQuery.replace(/\$(\d+)/g, '?');
|
|
191
|
+
const stmt = db.prepare(sqliteSql);
|
|
192
|
+
const ours = (params.length > 0 ? stmt.all(...params) : stmt.all());
|
|
193
|
+
db.close();
|
|
194
|
+
return { oracle, ours };
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
await conn.end();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
await server.stop();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=oracle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oracle.js","sourceRoot":"","sources":["../../../src/pg-sqlite-compiler/test/oracle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema metadata available to passes for type-aware translation.
|
|
3
|
+
*
|
|
4
|
+
* Implementations are caller-provided. The default NOOP_SCHEMA returns nothing
|
|
5
|
+
* and passes degrade gracefully (regex/AST-shape fallbacks).
|
|
6
|
+
*
|
|
7
|
+
* For orez, the eventual implementation will read from `ctx.storage.sql`
|
|
8
|
+
* PRAGMA + the `_orez_pg_metadata` catalog table.
|
|
9
|
+
*/
|
|
10
|
+
export interface SchemaInfo {
|
|
11
|
+
/** PG type name (e.g. "jsonb", "text[]") for a column, or undefined. */
|
|
12
|
+
getColumnType(schema: string, table: string, column: string): string | undefined;
|
|
13
|
+
/** ENUM definition lookup by PG type name. */
|
|
14
|
+
getEnum(typeName: string): EnumInfo | undefined;
|
|
15
|
+
/** Validate an ENUM literal value. */
|
|
16
|
+
isEnumValue(typeOid: number, label: string): boolean;
|
|
17
|
+
/** Column list for a table (for SELECT * expansion, RETURNING * etc.). */
|
|
18
|
+
getTableColumns(schema: string, table: string): readonly string[] | undefined;
|
|
19
|
+
}
|
|
20
|
+
export interface EnumInfo {
|
|
21
|
+
typeOid: number;
|
|
22
|
+
values: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
export interface CompileWarning {
|
|
25
|
+
kind: string;
|
|
26
|
+
message: string;
|
|
27
|
+
/** PG AST node tag where the warning was raised. */
|
|
28
|
+
near?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface CompileResult {
|
|
31
|
+
sql: string;
|
|
32
|
+
warnings: CompileWarning[];
|
|
33
|
+
}
|
|
34
|
+
export interface CompileOptions {
|
|
35
|
+
schema?: SchemaInfo;
|
|
36
|
+
pgVersion?: number;
|
|
37
|
+
/** Override pass list (mainly for testing individual passes). */
|
|
38
|
+
passes?: Pass[];
|
|
39
|
+
}
|
|
40
|
+
/** Context passed to every pass. */
|
|
41
|
+
export interface PassContext {
|
|
42
|
+
schema: SchemaInfo;
|
|
43
|
+
warnings: CompileWarning[];
|
|
44
|
+
/**
|
|
45
|
+
* Optional pass list — if set, runPasses uses these instead of the default
|
|
46
|
+
* pipeline. Otherwise the full default pipeline runs.
|
|
47
|
+
*/
|
|
48
|
+
passes?: Pass[];
|
|
49
|
+
}
|
|
50
|
+
/** A pass is a function that mutates a RawStmt in place. */
|
|
51
|
+
export interface Pass {
|
|
52
|
+
name: string;
|
|
53
|
+
run(rawStmt: any, ctx: PassContext): void;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/pg-sqlite-compiler/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAEhF,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IAE/C,sCAAsC;IACtC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IAEpD,0EAA0E;IAC1E,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CAC9E;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,cAAc,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iEAAiE;IACjE,MAAM,CAAC,EAAE,IAAI,EAAE,CAAA;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,UAAU,CAAA;IAClB,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,IAAI,EAAE,CAAA;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAAA;CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pg-sqlite-compiler/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.29",
|
|
4
4
|
"description": "PGlite-powered zero-sync development backend. No Docker required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,8 +44,7 @@
|
|
|
44
44
|
"orez": "dist/cli-entry.js"
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
|
-
"dist"
|
|
48
|
-
"src"
|
|
47
|
+
"dist"
|
|
49
48
|
],
|
|
50
49
|
"scripts": {
|
|
51
50
|
"build": "tsc && chmod +x dist/cli-entry.js",
|
|
@@ -58,6 +57,10 @@
|
|
|
58
57
|
"test:wasm": "vitest run src/wasm-sqlite.test.ts",
|
|
59
58
|
"test:all": "vitest run",
|
|
60
59
|
"test:watch": "vitest",
|
|
60
|
+
"test:compiler": "vitest run src/pg-sqlite-compiler/",
|
|
61
|
+
"test:compiler:oracle": "bun scripts/pgsqlite/ensure.ts && vitest run src/pg-sqlite-compiler/test/",
|
|
62
|
+
"compiler:harvest-fixtures": "bun scripts/pgsqlite/harvest-fixtures.ts",
|
|
63
|
+
"compiler:ensure-pgsqlite": "bun scripts/pgsqlite/ensure.ts",
|
|
61
64
|
"lint": "oxlint --import-plugin --ignore-pattern sqlite-wasm/",
|
|
62
65
|
"format": "oxfmt .",
|
|
63
66
|
"format:check": "oxfmt --check .",
|
|
@@ -83,7 +86,8 @@
|
|
|
83
86
|
"dependencies": {
|
|
84
87
|
"@electric-sql/pglite": "0.4.1",
|
|
85
88
|
"@electric-sql/pglite-tools": "^0.3.1",
|
|
86
|
-
"
|
|
89
|
+
"@pgsql/traverse": "17.2.6",
|
|
90
|
+
"bedrock-sqlite": "0.2.28",
|
|
87
91
|
"citty": "^0.2.0",
|
|
88
92
|
"pg-gateway": "0.3.0-beta.4",
|
|
89
93
|
"pgsql-parser": "^17.9.11",
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* integration tests for the admin data explorer endpoints.
|
|
3
|
-
*
|
|
4
|
-
* spins up pglite instances + admin server directly (no zero-cache)
|
|
5
|
-
* and exercises the /api/db/* and /api/sqlite/* endpoints.
|
|
6
|
-
*/
|
|
7
|
-
import { mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
8
|
-
import { resolve } from 'node:path'
|
|
9
|
-
|
|
10
|
-
import { PGlite } from '@electric-sql/pglite'
|
|
11
|
-
import { afterAll, beforeAll, describe, expect, test } from 'vitest'
|
|
12
|
-
|
|
13
|
-
import { startAdminServer } from './server.js'
|
|
14
|
-
|
|
15
|
-
import type { ZeroLiteConfig } from '../config.js'
|
|
16
|
-
import type { LogStore } from './log-store.js'
|
|
17
|
-
import type { Server } from 'node:http'
|
|
18
|
-
|
|
19
|
-
const TEST_PORT = 16400 + Math.floor(Math.random() * 500)
|
|
20
|
-
const DATA_DIR = `.orez-admin-data-test-${Date.now()}`
|
|
21
|
-
|
|
22
|
-
function stubLogStore(): LogStore {
|
|
23
|
-
const entries: any[] = []
|
|
24
|
-
return {
|
|
25
|
-
push() {},
|
|
26
|
-
query() {
|
|
27
|
-
return { entries, cursor: 0 }
|
|
28
|
-
},
|
|
29
|
-
getAll() {
|
|
30
|
-
return entries
|
|
31
|
-
},
|
|
32
|
-
clear() {
|
|
33
|
-
entries.length = 0
|
|
34
|
-
},
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function stubConfig(): ZeroLiteConfig {
|
|
39
|
-
return {
|
|
40
|
-
dataDir: resolve(DATA_DIR),
|
|
41
|
-
pgPort: 0,
|
|
42
|
-
zeroPort: 0,
|
|
43
|
-
adminPort: TEST_PORT,
|
|
44
|
-
pgUser: 'test',
|
|
45
|
-
pgPassword: 'test',
|
|
46
|
-
migrationsDir: '',
|
|
47
|
-
seedFile: '',
|
|
48
|
-
skipZeroCache: true,
|
|
49
|
-
disableWasmSqlite: false,
|
|
50
|
-
forceWasmSqlite: false,
|
|
51
|
-
useWorkerThreads: false,
|
|
52
|
-
singleDb: false,
|
|
53
|
-
readReplicas: 0,
|
|
54
|
-
logLevel: 'info',
|
|
55
|
-
pgliteOptions: {},
|
|
56
|
-
checkpointIntervalMs: 0,
|
|
57
|
-
maxLogFileSize: 0,
|
|
58
|
-
disableDiskLogs: true,
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
describe('admin data explorer', { timeout: 60_000 }, () => {
|
|
63
|
-
let server: Server
|
|
64
|
-
let postgres: PGlite
|
|
65
|
-
let cvr: PGlite
|
|
66
|
-
let cdb: PGlite
|
|
67
|
-
const base = `http://127.0.0.1:${TEST_PORT}`
|
|
68
|
-
|
|
69
|
-
beforeAll(async () => {
|
|
70
|
-
mkdirSync(DATA_DIR, { recursive: true })
|
|
71
|
-
|
|
72
|
-
postgres = new PGlite()
|
|
73
|
-
cvr = new PGlite()
|
|
74
|
-
cdb = new PGlite()
|
|
75
|
-
await Promise.all([postgres.waitReady, cvr.waitReady, cdb.waitReady])
|
|
76
|
-
|
|
77
|
-
// create test tables
|
|
78
|
-
await postgres.exec(`
|
|
79
|
-
CREATE TABLE public.users (
|
|
80
|
-
id serial PRIMARY KEY,
|
|
81
|
-
name text NOT NULL,
|
|
82
|
-
email text,
|
|
83
|
-
active boolean DEFAULT true
|
|
84
|
-
);
|
|
85
|
-
INSERT INTO public.users (name, email) VALUES
|
|
86
|
-
('alice', 'alice@test.com'),
|
|
87
|
-
('bob', 'bob@test.com'),
|
|
88
|
-
('charlie', 'charlie@test.com');
|
|
89
|
-
`)
|
|
90
|
-
|
|
91
|
-
await postgres.exec(`
|
|
92
|
-
CREATE TABLE public.posts (
|
|
93
|
-
id serial PRIMARY KEY,
|
|
94
|
-
user_id int REFERENCES users(id),
|
|
95
|
-
title text NOT NULL,
|
|
96
|
-
body text
|
|
97
|
-
);
|
|
98
|
-
INSERT INTO public.posts (user_id, title, body) VALUES
|
|
99
|
-
(1, 'hello world', 'first post'),
|
|
100
|
-
(1, 'second post', 'more content'),
|
|
101
|
-
(2, 'bob writes', NULL);
|
|
102
|
-
`)
|
|
103
|
-
|
|
104
|
-
server = await startAdminServer({
|
|
105
|
-
port: TEST_PORT,
|
|
106
|
-
logStore: stubLogStore(),
|
|
107
|
-
config: stubConfig(),
|
|
108
|
-
zeroEnv: {},
|
|
109
|
-
startTime: Date.now(),
|
|
110
|
-
db: { postgres, cvr, cdb, postgresReplicas: [] } as any,
|
|
111
|
-
})
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
afterAll(async () => {
|
|
115
|
-
server?.close()
|
|
116
|
-
await Promise.all([postgres?.close(), cvr?.close(), cdb?.close()])
|
|
117
|
-
rmSync(DATA_DIR, { recursive: true, force: true })
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
// --- html ---
|
|
121
|
-
|
|
122
|
-
test('GET / serves html', async () => {
|
|
123
|
-
const res = await fetch(`${base}/`)
|
|
124
|
-
expect(res.status).toBe(200)
|
|
125
|
-
expect(res.headers.get('content-type')).toContain('text/html')
|
|
126
|
-
const html = await res.text()
|
|
127
|
-
expect(html).toContain('oreZ admin')
|
|
128
|
-
expect(html).toContain('data-db="sqlite"')
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
test('GET /data serves html', async () => {
|
|
132
|
-
const res = await fetch(`${base}/data`)
|
|
133
|
-
expect(res.status).toBe(200)
|
|
134
|
-
const html = await res.text()
|
|
135
|
-
expect(html).toContain('sql-editor')
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
// --- /api/db/tables ---
|
|
139
|
-
|
|
140
|
-
test('lists postgres tables', async () => {
|
|
141
|
-
const res = await fetch(`${base}/api/db/tables?db=postgres`)
|
|
142
|
-
expect(res.status).toBe(200)
|
|
143
|
-
const data = await res.json()
|
|
144
|
-
expect(data.tables).toBeDefined()
|
|
145
|
-
const names = data.tables.map((t: any) => t.table_name)
|
|
146
|
-
expect(names).toContain('users')
|
|
147
|
-
expect(names).toContain('posts')
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
test('lists cvr tables (empty)', async () => {
|
|
151
|
-
const res = await fetch(`${base}/api/db/tables?db=cvr`)
|
|
152
|
-
expect(res.status).toBe(200)
|
|
153
|
-
const data = await res.json()
|
|
154
|
-
expect(data.tables).toBeDefined()
|
|
155
|
-
expect(data.tables.length).toBe(0)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
test('rejects unknown db name', async () => {
|
|
159
|
-
const res = await fetch(`${base}/api/db/tables?db=nope`)
|
|
160
|
-
expect(res.status).toBe(400)
|
|
161
|
-
const data = await res.json()
|
|
162
|
-
expect(data.error).toContain('unknown db')
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
// --- /api/db/table-data ---
|
|
166
|
-
|
|
167
|
-
test('browses table data', async () => {
|
|
168
|
-
const res = await fetch(`${base}/api/db/table-data?db=postgres&table=users`)
|
|
169
|
-
expect(res.status).toBe(200)
|
|
170
|
-
const data = await res.json()
|
|
171
|
-
expect(data.columns).toBeDefined()
|
|
172
|
-
expect(data.columns.length).toBeGreaterThanOrEqual(4)
|
|
173
|
-
expect(data.rows.length).toBe(3)
|
|
174
|
-
expect(data.total).toBe(3)
|
|
175
|
-
// check column metadata
|
|
176
|
-
const colNames = data.columns.map((c: any) => c.name)
|
|
177
|
-
expect(colNames).toContain('id')
|
|
178
|
-
expect(colNames).toContain('name')
|
|
179
|
-
expect(colNames).toContain('email')
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
test('table-data supports search', async () => {
|
|
183
|
-
const res = await fetch(
|
|
184
|
-
`${base}/api/db/table-data?db=postgres&table=users&search=alice`
|
|
185
|
-
)
|
|
186
|
-
const data = await res.json()
|
|
187
|
-
expect(data.rows.length).toBe(1)
|
|
188
|
-
expect(data.rows[0].name).toBe('alice')
|
|
189
|
-
expect(data.total).toBe(1)
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
test('table-data supports pagination', async () => {
|
|
193
|
-
const res = await fetch(
|
|
194
|
-
`${base}/api/db/table-data?db=postgres&table=users&limit=2&offset=0`
|
|
195
|
-
)
|
|
196
|
-
const data = await res.json()
|
|
197
|
-
expect(data.rows.length).toBe(2)
|
|
198
|
-
expect(data.total).toBe(3)
|
|
199
|
-
|
|
200
|
-
const page2 = await fetch(
|
|
201
|
-
`${base}/api/db/table-data?db=postgres&table=users&limit=2&offset=2`
|
|
202
|
-
)
|
|
203
|
-
const data2 = await page2.json()
|
|
204
|
-
expect(data2.rows.length).toBe(1)
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
test('table-data with schema-qualified name', async () => {
|
|
208
|
-
const res = await fetch(`${base}/api/db/table-data?db=postgres&table=public.posts`)
|
|
209
|
-
const data = await res.json()
|
|
210
|
-
expect(data.rows.length).toBe(3)
|
|
211
|
-
// check NULL values come through
|
|
212
|
-
const bobPost = data.rows.find((r: any) => r.title === 'bob writes')
|
|
213
|
-
expect(bobPost).toBeDefined()
|
|
214
|
-
expect(bobPost.body).toBeNull()
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
test('table-data missing table param', async () => {
|
|
218
|
-
const res = await fetch(`${base}/api/db/table-data?db=postgres`)
|
|
219
|
-
expect(res.status).toBe(400)
|
|
220
|
-
const data = await res.json()
|
|
221
|
-
expect(data.error).toContain('missing table')
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
// --- /api/db/query ---
|
|
225
|
-
|
|
226
|
-
test('runs arbitrary SQL', async () => {
|
|
227
|
-
const res = await fetch(`${base}/api/db/query`, {
|
|
228
|
-
method: 'POST',
|
|
229
|
-
headers: { 'Content-Type': 'application/json' },
|
|
230
|
-
body: JSON.stringify({
|
|
231
|
-
db: 'postgres',
|
|
232
|
-
sql: 'SELECT name, email FROM users ORDER BY name',
|
|
233
|
-
}),
|
|
234
|
-
})
|
|
235
|
-
expect(res.status).toBe(200)
|
|
236
|
-
const data = await res.json()
|
|
237
|
-
expect(data.fields).toEqual(['name', 'email'])
|
|
238
|
-
expect(data.rowCount).toBe(3)
|
|
239
|
-
expect(data.rows[0].name).toBe('alice')
|
|
240
|
-
expect(data.durationMs).toBeGreaterThanOrEqual(0)
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
test('returns error for bad SQL', async () => {
|
|
244
|
-
const res = await fetch(`${base}/api/db/query`, {
|
|
245
|
-
method: 'POST',
|
|
246
|
-
headers: { 'Content-Type': 'application/json' },
|
|
247
|
-
body: JSON.stringify({ db: 'postgres', sql: 'SELECT * FROM nonexistent' }),
|
|
248
|
-
})
|
|
249
|
-
expect(res.status).toBe(400)
|
|
250
|
-
const data = await res.json()
|
|
251
|
-
expect(data.error).toBeTruthy()
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
test('query with joins', async () => {
|
|
255
|
-
const res = await fetch(`${base}/api/db/query`, {
|
|
256
|
-
method: 'POST',
|
|
257
|
-
headers: { 'Content-Type': 'application/json' },
|
|
258
|
-
body: JSON.stringify({
|
|
259
|
-
db: 'postgres',
|
|
260
|
-
sql: `SELECT u.name, p.title FROM users u JOIN posts p ON p.user_id = u.id ORDER BY p.id`,
|
|
261
|
-
}),
|
|
262
|
-
})
|
|
263
|
-
const data = await res.json()
|
|
264
|
-
expect(data.rowCount).toBe(3)
|
|
265
|
-
expect(data.fields).toEqual(['name', 'title'])
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
test('query missing sql', async () => {
|
|
269
|
-
const res = await fetch(`${base}/api/db/query`, {
|
|
270
|
-
method: 'POST',
|
|
271
|
-
headers: { 'Content-Type': 'application/json' },
|
|
272
|
-
body: JSON.stringify({ db: 'postgres' }),
|
|
273
|
-
})
|
|
274
|
-
expect(res.status).toBe(400)
|
|
275
|
-
const data = await res.json()
|
|
276
|
-
expect(data.error).toContain('missing sql')
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
// --- sqlite ---
|
|
280
|
-
|
|
281
|
-
test('sqlite tables returns 404 when no replica', async () => {
|
|
282
|
-
const res = await fetch(`${base}/api/sqlite/tables`)
|
|
283
|
-
expect(res.status).toBe(404)
|
|
284
|
-
const data = await res.json()
|
|
285
|
-
expect(data.error).toContain('not found')
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
test('sqlite endpoints work when replica file exists', async () => {
|
|
289
|
-
// create a fake zero-replica.db using bedrock-sqlite
|
|
290
|
-
// @ts-expect-error - CJS module
|
|
291
|
-
const bedrock: any = await import('bedrock-sqlite')
|
|
292
|
-
const Ctor = bedrock.Database || bedrock.default?.Database || bedrock.default
|
|
293
|
-
const replicaPath = resolve(DATA_DIR, 'zero-replica.db')
|
|
294
|
-
const setupDb = new Ctor(replicaPath)
|
|
295
|
-
setupDb.exec(`
|
|
296
|
-
CREATE TABLE widgets (
|
|
297
|
-
id INTEGER PRIMARY KEY,
|
|
298
|
-
label TEXT NOT NULL,
|
|
299
|
-
count INTEGER
|
|
300
|
-
);
|
|
301
|
-
INSERT INTO widgets (label, count) VALUES
|
|
302
|
-
('alpha', 1),
|
|
303
|
-
('beta', 2),
|
|
304
|
-
('gamma', 3);
|
|
305
|
-
`)
|
|
306
|
-
setupDb.close()
|
|
307
|
-
|
|
308
|
-
// list tables
|
|
309
|
-
const tablesRes = await fetch(`${base}/api/sqlite/tables`)
|
|
310
|
-
expect(tablesRes.status).toBe(200)
|
|
311
|
-
const tables = await tablesRes.json()
|
|
312
|
-
expect(tables.tables.some((t: any) => t.name === 'widgets')).toBe(true)
|
|
313
|
-
|
|
314
|
-
// browse table data
|
|
315
|
-
const browseRes = await fetch(`${base}/api/sqlite/table-data?table=widgets`)
|
|
316
|
-
expect(browseRes.status).toBe(200)
|
|
317
|
-
const browse = await browseRes.json()
|
|
318
|
-
expect(browse.rows.length).toBe(3)
|
|
319
|
-
expect(browse.total).toBe(3)
|
|
320
|
-
|
|
321
|
-
// search
|
|
322
|
-
const searchRes = await fetch(
|
|
323
|
-
`${base}/api/sqlite/table-data?table=widgets&search=beta`
|
|
324
|
-
)
|
|
325
|
-
const search = await searchRes.json()
|
|
326
|
-
expect(search.rows.length).toBe(1)
|
|
327
|
-
expect(search.rows[0].label).toBe('beta')
|
|
328
|
-
|
|
329
|
-
// raw query
|
|
330
|
-
const queryRes = await fetch(`${base}/api/sqlite/query`, {
|
|
331
|
-
method: 'POST',
|
|
332
|
-
headers: { 'Content-Type': 'application/json' },
|
|
333
|
-
body: JSON.stringify({ sql: 'SELECT count(*) as c FROM widgets' }),
|
|
334
|
-
})
|
|
335
|
-
expect(queryRes.status).toBe(200)
|
|
336
|
-
const q = await queryRes.json()
|
|
337
|
-
expect(q.rows[0].c).toBe(3)
|
|
338
|
-
expect(q.fields).toContain('c')
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
// --- CORS ---
|
|
342
|
-
|
|
343
|
-
test('OPTIONS returns CORS headers', async () => {
|
|
344
|
-
const res = await fetch(`${base}/api/db/tables`, { method: 'OPTIONS' })
|
|
345
|
-
expect(res.status).toBe(200)
|
|
346
|
-
expect(res.headers.get('access-control-allow-origin')).toBe('*')
|
|
347
|
-
})
|
|
348
|
-
})
|