refactor(db): typed query helpers, shared test FormData helper
CI / check (push) Successful in 4m10s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m4s

Replace raw db.execute tuple casts with queryRows/rowsFrom/execResult/
affectedRows helpers from lib/db, drop redundant mysql2 casts on typed
query builders, and centralize per-test fakeForm into test/fake-form.
Update db mocks in tests so helpers resolve against mocked execute.
This commit is contained in:
openhands committed 2026-09-17 21:02:57 +02:00
1 parent 2e25b39364
commit 8638e81444
116 files changed
+866 -897

No files matched your search

+65 -63
View File
@@ -1,5 +1,5 @@
import { sql } from "drizzle-orm";
import { db } from "@/lib/db";
import { db, queryRows, rowsFrom } from "@/lib/db";
import {
type CatalogOfferRebuildResult,
type FurniDataReconcileResult,
@@ -56,11 +56,8 @@ const STR_COLS: Array<[string, string]> = [
["catalog_version_offers", "item_ids"],
];
type Rows<T> = [T[], unknown];
async function query<T>(q: unknown): Promise<T[]> {
const [rows] = (await db.execute(q as never)) as unknown as Rows<T>;
return rows ?? [];
return queryRows<T>(q as never);
}
async function exists(table: string): Promise<boolean> {
@@ -228,37 +225,33 @@ export async function countEmulatorIssues(): Promise<number> {
}
export async function listEmulatorIssues(): Promise<EmulatorCatalogIssue[]> {
const [items] = (await db.execute(sql`
const items = await queryRows<{
id: number;
pageId: number | string;
itemIds: string;
costCredits: number;
costPoints: number;
pointsType: number;
amount: number;
limitedStack: number;
limitedSells: number;
orderNumber: number;
}>(sql`
SELECT
ci.id, ci.page_id AS pageId, ci.item_ids AS itemIds,
ci.cost_credits AS costCredits, ci.cost_points AS costPoints,
ci.points_type AS pointsType, ci.amount, ci.limited_stack AS limitedStack,
ci.limited_sells AS limitedSells, ci.order_number AS orderNumber
FROM catalog_items ci
`)) as unknown as [
Array<{
id: number;
pageId: number | string;
itemIds: string;
costCredits: number;
costPoints: number;
pointsType: number;
amount: number;
limitedStack: number;
limitedSells: number;
orderNumber: number;
}>,
unknown,
];
const [pages] = (await db.execute(
`);
const pages = await queryRows<{
id: number;
parentId: number | string;
orderNum: number;
}>(
sql`SELECT id, parent_id AS parentId, order_num AS orderNum FROM catalog_pages`,
)) as unknown as [
Array<{ id: number; parentId: number | string; orderNum: number }>,
unknown,
];
const [bases] = (await db.execute(
sql`SELECT id FROM items_base`,
)) as unknown as [Array<{ id: number }>, unknown];
);
const bases = await queryRows<{ id: number }>(sql`SELECT id FROM items_base`);
const pageIds = new Set(pages.map((r) => Number(r.id)));
const baseIds = new Set(bases.map((r) => Number(r.id)));
@@ -504,13 +497,16 @@ export async function fixEmulatorIssues(): Promise<FixEmulatorResult> {
// duplicate sibling order -> re-number sequentially per page
const dupOrders = issues.filter((i) => i.type === "duplicate_order");
if (dupOrders.length > 0) {
const [allRows] = (await tx.execute(sql`
const allRows = rowsFrom<{
id: number;
pageId: number | string;
orderNumber: number;
}>(
await tx.execute(sql`
SELECT id, page_id AS pageId, order_number AS orderNumber
FROM catalog_items ORDER BY page_id, order_number, id
`)) as unknown as [
Array<{ id: number; pageId: number | string; orderNumber: number }>,
unknown,
];
`),
);
let lastPage: number | null = null;
let next = 1;
for (const row of allRows) {
@@ -535,12 +531,14 @@ export async function fixEmulatorIssues(): Promise<FixEmulatorResult> {
const cyclePages = issues.filter((i) => i.type === "page_cycle");
if (cyclePages.length > 0) {
const cycleIds = cyclePages.map((i) => i.id);
const [cycleRows] = (await tx.execute(sql`
const cycleRows = rowsFrom<{
id: number | string;
parentId: number | string;
}>(
await tx.execute(sql`
SELECT id, parent_id AS parentId FROM catalog_pages WHERE id IN (${sql.join(cycleIds, sql`, `)})
`)) as unknown as [
Array<{ id: number | string; parentId: number | string }>,
unknown,
];
`),
);
const parentOf = new Map(
cycleRows.map((r) => [Number(r.id), Number(r.parentId)]),
);
@@ -574,13 +572,15 @@ export async function fixEmulatorIssues(): Promise<FixEmulatorResult> {
if (pageOrderIssues.length > 0) {
const parents = [...new Set(pageOrderIssues.map((i) => i.pageId))];
for (const parent of parents) {
const [children] = (await tx.execute(sql`
const children = rowsFrom<{
id: number | string;
orderNum: number;
}>(
await tx.execute(sql`
SELECT id, order_num AS orderNum FROM catalog_pages
WHERE parent_id = ${String(parent)} ORDER BY order_num, id
`)) as unknown as [
Array<{ id: number | string; orderNum: number }>,
unknown,
];
`),
);
let next = 1;
for (const child of children) {
const newOrder = next++;
@@ -659,19 +659,19 @@ export async function dedupeCatalogPages(): Promise<CatalogPageDedupResult> {
if (pages.length < 2) continue;
const [winner, ...losers] = pages.map((p) => p.id);
for (const loser of losers) {
const ci = (await tx.execute(sql`
UPDATE catalog_items SET page_id = ${winner} WHERE page_id = ${loser}`)) as unknown as [
Record<string, unknown>,
unknown,
];
remapped += Number(ci[0]?.affectedRows ?? 0);
const affected =
rowsFrom<{ affectedRows: number }>(
await tx.execute(sql`
UPDATE catalog_items SET page_id = ${winner} WHERE page_id = ${loser}`),
)[0]?.affectedRows ?? 0;
remapped += Number(affected);
if (await exists("catalog_items_bc")) {
const bci = (await tx.execute(sql`
UPDATE catalog_items_bc SET page_id = ${winner} WHERE page_id = ${loser}`)) as unknown as [
Record<string, unknown>,
unknown,
];
remapped += Number(bci[0]?.affectedRows ?? 0);
const bciAffected =
rowsFrom<{ affectedRows: number }>(
await tx.execute(sql`
UPDATE catalog_items_bc SET page_id = ${winner} WHERE page_id = ${loser}`),
)[0]?.affectedRows ?? 0;
remapped += Number(bciAffected);
}
// Keep any child pages by reparenting them onto the survivor.
await tx.execute(sql`
@@ -738,10 +738,12 @@ export async function removeDuplicateItemsBase(): Promise<DedupResult> {
const ids = g.ids.filter((x) => x !== g.keeper);
for (const dupId of ids) {
for (const [table, col] of activeInt) {
const r = (await tx.execute(
sql`UPDATE ${sql.raw(table)} t SET ${sql.raw(col)} = ${g.keeper} WHERE ${sql.raw(col)} = ${dupId}`,
)) as unknown as [Record<string, unknown>, unknown];
remapped += Number(r[0]?.affectedRows ?? 0);
const r = rowsFrom<{ affectedRows: number }>(
await tx.execute(
sql`UPDATE ${sql.raw(table)} t SET ${sql.raw(col)} = ${g.keeper} WHERE ${sql.raw(col)} = ${dupId}`,
),
)[0];
remapped += Number(r?.affectedRows ?? 0);
}
for (const [table, col] of activeStr) {
const from = String(dupId);
@@ -783,12 +785,12 @@ async function dedupCatalogTable(table: string): Promise<number> {
? ["page_id", "item_ids", "catalog_name", "extradata"]
: ["page_id", "item_ids"];
const on = grp.map((c) => `t.${c} <=> d.${c}`).join(" AND ");
const r = (await db.execute(
const r = await queryRows<{ affectedRows: number }>(
sql.raw(
`DELETE t FROM ${table} t JOIN (SELECT ${grp.join(",")}, MIN(id) AS _keep FROM ${table} GROUP BY ${grp.join(",")} HAVING COUNT(*) > 1) d ON ${on} WHERE t.id <> d._keep`,
),
)) as unknown as [Record<string, unknown>, unknown];
return Number(r[0]?.affectedRows ?? 0);
);
return r[0]?.affectedRows ?? 0;
}
/**