fix(14): revise plans based on checker feedback

This commit is contained in:
2026-04-04 12:09:49 +02:00
parent 88708f962a
commit 30ec9b92d1
5 changed files with 140 additions and 79 deletions

View File

@@ -90,7 +90,7 @@ Conversion rules (apply to ALL service files):
<action>
Convert each service file following the async conversion rules. Read each file fully before modifying.
**item.service.ts** 5 exported functions (getAllItems, getItemById, createItem, updateItem, duplicateItem, deleteItem):
**item.service.ts** -- 5 exported functions (getAllItems, getItemById, createItem, updateItem, duplicateItem, deleteItem):
- `getAllItems`: `async`, remove `.all()`, `return await db.select()...`
- `getItemById`: `async`, replace `.get() ?? null` with `const [row] = await db.select()...; return row ?? null`
- `createItem`: `async`, replace `.returning().get()` with `const [row] = await db.insert()...returning(); return row`
@@ -98,29 +98,29 @@ Convert each service file following the async conversion rules. Read each file f
- `duplicateItem`: `async`, same pattern as createItem
- `deleteItem`: `async`, existence check `const [item] = await db.select()...`, delete `await db.delete()...`
**category.service.ts** Has a transaction in `deleteCategory` (moves items to Uncategorized then deletes):
**category.service.ts** -- Has a transaction in `deleteCategory` (moves items to Uncategorized then deletes):
- All functions: `async`
- Transaction: `await db.transaction(async (tx) => { await tx.update()...; await tx.delete()...; })`
- All `.all()` -> remove, `.get()` -> destructure, `.run()` -> remove
**thread.service.ts** Has transactions in `resolveThread` and `unresolveThread`:
**thread.service.ts** -- Has transactions in `resolveThread` and `unresolveThread`:
- All functions: `async`
- `resolveThread` transaction: `await db.transaction(async (tx) => { ... })` with all inner operations awaited
- `unresolveThread` transaction: same pattern
- `.all()` -> remove, `.get()` -> destructure, `.run()` -> remove
- `.returning().get()` -> `const [row] = await ...returning()`
**setup.service.ts** Has a transaction in `updateSetupItems` (delete all + re-insert):
**setup.service.ts** -- Has a transaction in `updateSetupItems` (delete all + re-insert):
- All functions: `async`
- Transaction: `await db.transaction(async (tx) => { await tx.delete()...; for (const item of items) { await tx.insert()...; } })`
- `.all()` -> remove, `.get()` -> destructure, `.run()` -> remove
**totals.service.ts** Read-only aggregate queries:
**totals.service.ts** -- Read-only aggregate queries:
- All functions: `async`
- Remove `.all()`, `.get()` -> destructure
</action>
<verify>
<automated>! grep -n "\.all()\|\.get()\|\.run()" src/server/services/item.service.ts src/server/services/category.service.ts src/server/services/thread.service.ts src/server/services/setup.service.ts src/server/services/totals.service.ts && grep -c "async function" src/server/services/item.service.ts | grep -q "[3-9]" && echo "PASS" || echo "FAIL"</automated>
<automated>! grep -n "\.all()\|\.get()\|\.run()" src/server/services/item.service.ts src/server/services/category.service.ts src/server/services/thread.service.ts src/server/services/setup.service.ts src/server/services/totals.service.ts && grep -c "async function" src/server/services/item.service.ts | grep -q "[3-9]" && bun run lint 2>&1 | tail -3 && echo "PASS" || echo "FAIL"</automated>
</verify>
<acceptance_criteria>
- item.service.ts: every exported function starts with `export async function`
@@ -135,30 +135,30 @@ Convert each service file following the async conversion rules. Read each file f
</task>
<task type="auto">
<name>Task 2: Convert auth/oauth/csv/image services and update server index</name>
<name>Task 2: Convert auth/oauth/csv/image services, update server index, and run PGlite smoke test</name>
<files>src/server/services/auth.service.ts, src/server/services/oauth.service.ts, src/server/services/csv.service.ts, src/server/services/image.service.ts, src/server/index.ts</files>
<read_first>src/server/services/auth.service.ts, src/server/services/oauth.service.ts, src/server/services/csv.service.ts, src/server/services/image.service.ts, src/server/index.ts</read_first>
<action>
**auth.service.ts** User and session management:
**auth.service.ts** -- User and session management:
- All functions: `async`
- Remove `.all()`, `.get()` -> destructure, `.run()` -> remove
- `.returning().get()` -> `const [row] = await ...returning()`
- Pay attention to boolean checks on `oauthCodes.used` the column is now native `boolean` (true/false), not integer (0/1). If any code checks `=== 0` or `=== 1` for the `used` field, change to `=== false` or `=== true`.
- Pay attention to boolean checks on `oauthCodes.used` -- the column is now native `boolean` (true/false), not integer (0/1). If any code checks `=== 0` or `=== 1` for the `used` field, change to `=== false` or `=== true`.
**oauth.service.ts** OAuth client, code, token management:
**oauth.service.ts** -- OAuth client, code, token management:
- All functions: `async`
- Same conversion patterns
- IMPORTANT: The `used` column on `oauthCodes` is now `boolean` type. Any `.set({ used: 1 })` must become `.set({ used: true })`. Any `.where(eq(oauthCodes.used, 0))` must become `.where(eq(oauthCodes.used, false))`.
**csv.service.ts** CSV export:
**csv.service.ts** -- CSV export:
- All functions: `async`
- This is read-only, straightforward `.all()` removal
**image.service.ts** Image handling:
**image.service.ts** -- Image handling:
- All functions: `async`
- Same conversion patterns. May have fewer DB calls than other services.
**src/server/index.ts** Server startup:
**src/server/index.ts** -- Server startup:
- Change `seedDefaults()` to `await seedDefaults()` at the top level
- Since the file is a module (ESM), top-level await is supported. Wrap the seed call:
```typescript
@@ -167,22 +167,39 @@ Convert each service file following the async conversion rules. Read each file f
```
- If the file structure does not support top-level await cleanly (e.g., exports are synchronous), wrap in an async IIFE or move the await before the export.
- The `seedDefaults` import already points to the async version from Plan 01.
**After all conversions, run a PGlite smoke test to verify at least one service works end-to-end:**
```bash
bun -e "
import { createTestDb } from './tests/helpers/db.ts';
import * as schema from './src/db/schema.ts';
const db = await createTestDb();
// Test a basic item service operation
const { createItem } = await import('./src/server/services/item.service.ts');
const [cat] = await db.select().from(schema.categories);
const item = await createItem(db as any, { name: 'Smoke Test', categoryId: cat.id, quantity: 1 });
if (!item || !item.id) { console.error('FAIL: createItem returned no result'); process.exit(1); }
console.log('Service smoke test PASSED: item created with id', item.id);
"
```
This validates that the async conversion is actually functional, not just structurally correct.
</action>
<verify>
<automated>! grep -n "\.all()\|\.get()\|\.run()" src/server/services/auth.service.ts src/server/services/oauth.service.ts src/server/services/csv.service.ts src/server/services/image.service.ts && grep -q "await seedDefaults" src/server/index.ts && echo "PASS" || echo "FAIL"</automated>
<automated>! grep -n "\.all()\|\.get()\|\.run()" src/server/services/auth.service.ts src/server/services/oauth.service.ts src/server/services/csv.service.ts src/server/services/image.service.ts && grep -q "await seedDefaults" src/server/index.ts && bun run lint 2>&1 | tail -3 && echo "PASS" || echo "FAIL"</automated>
</verify>
<acceptance_criteria>
- auth.service.ts: every exported function is `async`
- auth.service.ts: does NOT contain `.all()`, `.get()`, or `.run()`
- oauth.service.ts: every exported function is `async`
- oauth.service.ts: does NOT contain `.set({ used: 1 })` uses `.set({ used: true })` instead
- oauth.service.ts: does NOT contain `eq(oauthCodes.used, 0)` uses `eq(oauthCodes.used, false)` instead
- oauth.service.ts: does NOT contain `.set({ used: 1 })` -- uses `.set({ used: true })` instead
- oauth.service.ts: does NOT contain `eq(oauthCodes.used, 0)` -- uses `eq(oauthCodes.used, false)` instead
- csv.service.ts: every exported function is `async`, no `.all()` calls
- image.service.ts: every exported function is `async`
- src/server/index.ts: contains `await seedDefaults()`
- No file in this set contains `.all()`, `.get()`, or `.run()` calls on db objects
- PGlite smoke test creating an item via service function exits 0
</acceptance_criteria>
<done>Auth, OAuth, CSV, and image services fully async. OAuth boolean conversion complete. Server startup awaits async seed.</done>
<done>Auth, OAuth, CSV, and image services fully async. OAuth boolean conversion complete. Server startup awaits async seed. PGlite smoke test confirms services work against async DB.</done>
</task>
</tasks>
@@ -191,10 +208,12 @@ Convert each service file following the async conversion rules. Read each file f
- `grep -rn "\.all()\|\.get()\|\.run()" src/server/services/` returns NO matches (except possibly string literals in error messages)
- `grep -c "async function" src/server/services/*.ts` shows every service has async functions
- `grep "await seedDefaults" src/server/index.ts` returns a match
- `bun run lint` passes
- PGlite smoke test exits 0
</verification>
<success_criteria>
All 9 service files use async/await for every database operation. No SQLite-only methods (.all, .get, .run) remain. Transactions use async callbacks. OAuth boolean conversion complete. Server index awaits async seed.
All 9 service files use async/await for every database operation. No SQLite-only methods (.all, .get, .run) remain. Transactions use async callbacks. OAuth boolean conversion complete. Server index awaits async seed. PGlite smoke test validates at least one service works end-to-end. Lint passes.
</success_criteria>
<output>