feat(pwa): move app.logicsrc.com from Turso/libSQL to Postgres via @profullstack/libsql-pg

Production reads DATABASE_URL (postgres://) through @profullstack/libsql-pg,
which keeps the @libsql/client surface; no query changed. A missing,
non-Postgres or leftover libsql:// URL fails at boot in production. Dev and
tests keep libSQL (file:/:memory:) as a devDependency. Postgres migrations in
src/migrations-pg/ (converted with libsql-pg convert-schema, same file names
so the copied _migrations ledger matches); migrate.mjs picks the dialect.
Tests run against Postgres with PWA_TEST_DATABASE_URL.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-09-25 16:25:23 +00:00
parent c535aaa32c
commit 9e16923a89
12 changed files with 507 additions and 29 deletions

View file

@ -7,7 +7,7 @@
//
// Runs against an in-memory libSQL database, so DATABASE_URL must be set before
// anything imports db.mjs.
process.env.DATABASE_URL = ":memory:";
process.env.DATABASE_URL = process.env.PWA_TEST_DATABASE_URL || ":memory:";
import test from "node:test";
import assert from "node:assert/strict";
@ -17,13 +17,19 @@ import { dirname, join } from "node:path";
import express from "express";
const here = dirname(fileURLToPath(import.meta.url));
const { db, run, get, all } = await import("../src/db.mjs");
const { db, run, get, all, isPostgres } = await import("../src/db.mjs");
const { credshareRouter } = await import("../src/routes/credshare.mjs");
/** Apply the schema this router depends on. */
async function migrate() {
// Against a real Postgres (PWA_TEST_DATABASE_URL) start from an empty schema;
// run with --test-concurrency=1 then, the files share one database.
if (isPostgres) {
await db.execute("DROP SCHEMA public CASCADE");
await db.execute("CREATE SCHEMA public");
}
for (const file of ["001_auth.sql", "002_credshare.sql"]) {
const sql = readFileSync(join(here, "..", "src", "migrations", file), "utf8");
const sql = readFileSync(join(here, "..", "src", isPostgres ? "migrations-pg" : "migrations", file), "utf8");
for (const statement of sql.split(/;\s*$/m).map((s) => s.trim()).filter(Boolean)) {
await db.execute(statement);
}

View file

@ -13,7 +13,7 @@
//
// Runs against an in-memory libSQL database, so DATABASE_URL must be set before
// anything imports db.mjs.
process.env.DATABASE_URL = ":memory:";
process.env.DATABASE_URL = process.env.PWA_TEST_DATABASE_URL || ":memory:";
import test from "node:test";
import assert from "node:assert/strict";
@ -23,12 +23,18 @@ import { dirname, join } from "node:path";
import express from "express";
const here = dirname(fileURLToPath(import.meta.url));
const { db, run } = await import("../src/db.mjs");
const { db, run, isPostgres } = await import("../src/db.mjs");
const { credshareRouter } = await import("../src/routes/credshare.mjs");
async function migrate() {
// Against a real Postgres (PWA_TEST_DATABASE_URL) start from an empty schema;
// run with --test-concurrency=1 then, the files share one database.
if (isPostgres) {
await db.execute("DROP SCHEMA public CASCADE");
await db.execute("CREATE SCHEMA public");
}
for (const file of ["001_auth.sql", "002_credshare.sql"]) {
const sql = readFileSync(join(here, "..", "src", "migrations", file), "utf8");
const sql = readFileSync(join(here, "..", "src", isPostgres ? "migrations-pg" : "migrations", file), "utf8");
for (const statement of sql.split(/;\s*$/m).map((s) => s.trim()).filter(Boolean)) {
await db.execute(statement);
}

View file

@ -1,6 +1,6 @@
// Integration coverage for team-member CRUD, invite-key rotation, and the
// dashboard controls that expose those operations.
process.env.DATABASE_URL = ":memory:";
process.env.DATABASE_URL = process.env.PWA_TEST_DATABASE_URL || ":memory:";
import test from "node:test";
import assert from "node:assert/strict";
@ -10,13 +10,19 @@ import { dirname, join } from "node:path";
import express from "express";
const here = dirname(fileURLToPath(import.meta.url));
const { db, run, get } = await import("../src/db.mjs");
const { db, run, get, isPostgres } = await import("../src/db.mjs");
const { sha256 } = await import("../src/lib/crypto.mjs");
const { credshareRouter } = await import("../src/routes/credshare.mjs");
const { pagesRouter } = await import("../src/routes/pages.mjs");
// Against a real Postgres (PWA_TEST_DATABASE_URL) start from an empty schema;
// run with --test-concurrency=1 then, the files share one database.
if (isPostgres) {
await db.execute("DROP SCHEMA public CASCADE");
await db.execute("CREATE SCHEMA public");
}
for (const file of ["001_auth.sql", "002_credshare.sql"]) {
const sql = readFileSync(join(here, "..", "src", "migrations", file), "utf8");
const sql = readFileSync(join(here, "..", "src", isPostgres ? "migrations-pg" : "migrations", file), "utf8");
for (const statement of sql.split(/;\s*$/m).map((s) => s.trim()).filter(Boolean)) await db.execute(statement);
}