11 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 14-postgresql-migration | 03 | execute | 2 |
|
|
true |
|
|
Purpose: Services are the data access layer. Every database call must be async for postgres.js. This is the bulk of the mechanical conversion work (~82 call sites per the research). Output: All service files use async/await. Server index awaits seed.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/14-postgresql-migration/14-CONTEXT.md @.planning/phases/14-postgresql-migration/14-RESEARCH.md @.planning/phases/14-postgresql-migration/14-01-SUMMARY.md@src/db/schema.ts @src/db/index.ts
Conversion rules (apply to ALL service files):
function foo(db)->async function foo(db).all()-> remove (await the query directly, returns array).get()-> destructure:const [row] = await db.select()....run()-> remove (await the query directly).returning().get()->const [row] = await db.insert()...returning()db.transaction(() => { ... })->await db.transaction(async (tx) => { await tx... })
item.service.ts — 5 exported functions (getAllItems, getItemById, createItem, updateItem, duplicateItem, deleteItem):
getAllItems:async, remove.all(),return await db.select()...getItemById:async, replace.get() ?? nullwithconst [row] = await db.select()...; return row ?? nullcreateItem:async, replace.returning().get()withconst [row] = await db.insert()...returning(); return rowupdateItem:async, existence check uses destructureconst [existing] = await db.select()..., update usesconst [row] = await db.insert()...returning(); return rowduplicateItem:async, same pattern as createItemdeleteItem:async, existence checkconst [item] = await db.select()..., deleteawait db.delete()...
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:
- All functions:
async resolveThreadtransaction:await db.transaction(async (tx) => { ... })with all inner operations awaitedunresolveThreadtransaction: 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):
- 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:
- All functions:
async - Remove
.all(),.get()-> destructure ! 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" <acceptance_criteria>- item.service.ts: every exported function starts with
export async function - item.service.ts: does NOT contain
.all(),.get(), or.run() - category.service.ts:
deleteCategorycontainsawait db.transaction(async (tx) => - thread.service.ts:
resolveThreadandunresolveThreadcontainawait db.transaction(async (tx) => - setup.service.ts:
updateSetupItemscontainsawait db.transaction(async (tx) => - totals.service.ts: every exported function is async
- No file in this set contains
.all(),.get(), or.run()calls on db/tx objects </acceptance_criteria> Core data services (item, category, thread, setup, totals) fully converted to async with all SQLite-only methods removed.
- item.service.ts: every exported function starts with
oauth.service.ts — OAuth client, code, token management:
- All functions:
async - Same conversion patterns
- IMPORTANT: The
usedcolumn onoauthCodesis nowbooleantype. 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:
- All functions:
async - This is read-only, straightforward
.all()removal
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:
- Change
seedDefaults()toawait seedDefaults()at the top level - Since the file is a module (ESM), top-level await is supported. Wrap the seed call:
// Seed default data on startup await seedDefaults(); - 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
seedDefaultsimport already points to the async version from Plan 01. ! 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" <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)— useseq(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 </acceptance_criteria> Auth, OAuth, CSV, and image services fully async. OAuth boolean conversion complete. Server startup awaits async seed.
- auth.service.ts: every exported function is
<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. </success_criteria>
After completion, create `.planning/phases/14-postgresql-migration/14-03-SUMMARY.md`