feat(wallet-sdk): transactions slice (step 8) - #1175
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s and the web flip Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
…erage Review follow-up: the page-end rule moved into TransactionRepository.list without a direct test, and acknowledge lacked a mid-call abort test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7639966 to
abc84f8
Compare
Marketplace adversarial review (maxplayer job 5c7bc43b…, seller milibot)Verdict: READY — 0 Critical, 0 Important, 2 Minor, 2 Nit. Full report below, then the orchestrator's triage. Triage
Review: Step 8 — wire
|
Delivered via a maxplayer contribution-mode job (seller forked the repo and read the context itself; no source pasting). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Step 8 of the 19-step no-cache wallet-SDK extraction (spec:
docs/superpowers/specs/2026-06-24-wallet-sdk-no-cache-production-design.md; plan committed in this PR:docs/superpowers/plans/2026-08-12-wallet-sdk-transactions-slice.md). This wires thetransactionsnamespace of the SDK contract and flips the web transactions feature from@agicash/wallet-sdk/temporaryimports tosdk.transactions.*.What changed
packages/wallet-sdk/domain/transactions/transactions-api.ts—createTransactionsApi(deps): session-fenced wrapper over the existingTransactionRepository(accounts-style async repository construction viaawait keys.getEncryption(); signal captured before the await, re-checked after each await;abortSignalthreaded into every repository call). Methods:get,list,countPendingAck,acknowledge.packages/wallet-sdk/domain/transactions/transactions-api.test.ts— 12 tests: per-method delegation/userId injection plus the session-fence matrix (NoSessionError,SessionEndedErrorafter dispose with zero repository calls,SessionEndedErrorwhen the session ends inside the async repository factory,SessionEndedErrormid-read).domain/sdk/transactions.ts— the contract'sTransaction = Omit<DomainTransaction, 'userId'>projection is deleted and the domainTransactionunion is used directly.listgained JSDoc documenting the RPC semantics (DRAFT/FAILED excluded, ordering, default pageSize 25,nextCursor === nullon the last page).domain/transactions/transaction.ts—userIddeleted fromBaseTransactionSchema;toTransactionstops mappingdata.user_id.domain/transactions/transaction-repository.ts—Cursornormalized to the non-null keyset tuple (| nullmoved to use sites); the page-end rule folded intolist(nextCursorisnullwhen the page is short).domain/sdk/sdk.ts— the throwingtransactionsgetter replaced with a readonly field wired viacreateTransactionsApi({ db, getSession: getLiveSession, keys }).domain/sdk/events.ts—Transactionimport flipped from the contract file to the domain entity (contacts precedent).apps/web-wallet/.../transaction-hooks.ts—useTransaction,useTransactions,useHasTransactionsPendingAck,useAcknowledgeTransactionnow callsdk.transactions.*;useUserdependency dropped (session is SDK-internal); query keys, staleTime, refetch options, retry counts unchanged; the hook's manual page-end rule removed (now SDK-side);Cursor+NotFoundErrorimports moved from/temporaryto the public root.packages/wallet-sdk/temporary.ts— 28 dead transaction re-exports pruned (enum schemas, transaction schemas, all per-variant details schemas/parsers, details types, parser, parser input/shape types, and post-flipCursor).AgicashDbTransaction+TransactionRepositorystay (live realtime consumers).Design decisions
userIddeleted from the domain entity: nothing readstransaction.userId; every otheruserIdin the domain is an input parameter; ownership = session + RLS.Omitover a discriminated union collapses it to a flat object type and breaks narrowing (e.g.transaction-list.tsxnarrowstype === 'CASHU_LIGHTNING' && direction === 'SEND'to reachdetails.destinationDetails), so the contract uses the domainTransactionunion directly — same resolution as the contacts slice.Cursornormalization: the repositoryCursortype is the non-null keyset tuple; nullability lives at use sites.list: headless consumers get true end-of-list semantics (nextCursor === null) instead of each caller re-deriving it.transaction.created/transaction.updatedstay type-only until the step-18 realtime feed./temporary:useTransactionChangeHandlers+useTransactionRepository(needs instancetoTransactiondecryption) anduseReverseTransaction(unmigrated send domain) are intentionally unchanged until steps 14/18.Verification
bun run fix:allexit 0;bun run typecheckexit 0 (8 packages).sdk.transactions.list(empty state + real row); full receive money-path on a test mint created a real transaction end-to-end; the detail page rendered it throughsdk.transactions.get; auto-acknowledge ran throughsdk.transactions.acknowledge; pending-ack badge query throughsdk.transactions.countPendingAck; zero console errors.Delegation note
Implementation, tests, web flip, the temporary.ts prune, this description, and the adversarial review were produced as paid maxplayer marketplace jobs and integrated + verified locally by the orchestrating agent.
🤖 Generated with Claude Code