feat(wallet-sdk): contacts slice (step 7) - #1174
Conversation
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| * @param value - The value to check | ||
| * @returns True if the value is a valid Contact, false otherwise | ||
| */ | ||
| export const isContact = (value: unknown): value is Contact => { |
There was a problem hiding this comment.
It lost its last consumer in this PR (deviation 3 in the plan doc). At master, isContact had two consumers: the dead temporary.ts re-export (the web never imported it; pruned in d3973e0) and resolveSendDestination.
This PR retypes that seam's input to the public Contact. The zod guard validates the domain schema, which requires ownerId — a public-shaped contact fails the guard, falls into the string branch, and breaks resolution. typeof input !== 'string' narrows string | Contact exactly. With zero consumers left, the guard was deleted.
After d78a3b5 (thread above), api contacts also lack ownerId at runtime, so the old guard would now reject every real contact.
There was a problem hiding this comment.
Outcome: restored in b2092ae. The deletion reason expired — with ownerId gone from ContactSchema (entity collapse, thread above) the guard accepts the seam shape again. It replaces the typeof narrowing: a positive guard keeps the else-branch honest if the input union ever gains a third member.
…e web flip Wraps the contacts domain in the sdk.contacts contract namespace and flips the web contacts feature off @agicash/wallet-sdk/temporary. - createContactsApi: session-fenced per the accounts pattern (requireUserId + sessionSignal pre/post checks); repository built once; findContactCandidates returns UserProfile[] — candidates are profiles, a contact id exists only after create. - ownerId is deleted from the Contact entity: nothing read it (ownership lives in query params + RLS), so one Contact type serves as entity and contract type, exported from the package root. - Send seam: resolve-destination narrows string | Contact with the isContact guard against the ownerId-free schema. - ContactRepository.get: maybeSingle + null on missing + abortSignal. - Web contact hooks call sdk.contacts.*; query keys and cache semantics unchanged; realtime invalidation stays on /temporary until step 18; contact-repository-hooks deleted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b2092ae to
4e50a83
Compare
Step 7 of the no-cache SDK extraction (spec:
docs/superpowers/specs/2026-06-24-wallet-sdk-no-cache-production-design.md). Wraps the already-moved contacts domain in thesdk.contactscontract namespace and flips the web contacts feature off@agicash/wallet-sdk/temporary.Plan + recorded execution deviations:
docs/superpowers/plans/2026-08-11-wallet-sdk-contacts-slice.md.SDK
domain/sdk/contacts.ts):CreateContactParams = { username }(ownerId implicit from the session);findContactCandidates(query): Promise<UserProfile[]>— deviation from the step-4 stub'sContact[]: candidates are user profiles, a contact id exists only aftercreate.createContactsApi(domain/contacts/contacts-api.ts): session-fenced per the accounts pattern (requireUserId+sessionSignalpre/post checks); repository constructed once (db + lud16 domain are process-stable; contacts rows are plaintext, no key material). 11 bun tests.ContactRepository.get→.maybeSingle()+nullon missing (contract:Contact | null) +abortSignaloption.sdk.ts:contactsgetter no longer throwsNotImplementedError; wired withconfig.lightningAddressDomain(its first real consumer).Contactprojection surfaced: dropped the shadowingexport type { Contact }fromindex.ts(per that file's slice-deletes-its-names mechanism). Review follow-up (5cd8577, 366fa51):ownerIdis deleted from the domain entity itself — nothing read it (ownership lives in query params + RLS), so oneContacttype serves as both entity and contract type. NoOmit, no runtime mapper; the rootindex.tsdomain-type export is back as the sole origin (it no longer shadows a projection), and the web realtime echo (toContact) produces the exact contract shape.resolve-destination.tstakes theContacttype and narrows with the zodisContactguard. The guard was deleted mid-slice — while the schema still carriedownerIdit would have misrouted public-shaped contacts into string parsing — and restored in b2092ae after the entity collapse made the schema match the seam shape; positive narrowing keeps the else-branch honest if the input union ever grows.Web
contact-hooks.ts:useContacts/useCreateContact/useDeleteContact/useFindContactCandidatesnow callsdk.contacts.*; query keys and cache semantics unchanged.ContactsCacheanduseContactChangeHandlersstay as-is — the realtime invalidation layer flips in step 18 (accounts precedent), so the handlers keep their two/temporaryimports.contact-repository-hooks.tsdeleted (last consumer gone); unusedisContactre-export pruned fromtemporary.ts.Verification
bun run fix:all,bun run typecheck(all packages),bun run test— all exit 0 (SDK 143 tests incl. 11 new; web 38).CONTACT_CREATEDecho — the sole cache-add path), detail page, delete. Zero console errors.🤖 Generated with Claude Code