Skip to content

feat(teams): add createResendInvitationSender for the invitations seam#143

Merged
vutuanlinh2k2 merged 1 commit into
mainfrom
feat/teams-resend-sender
Jun 25, 2026
Merged

feat(teams): add createResendInvitationSender for the invitations seam#143
vutuanlinh2k2 merged 1 commit into
mainfrom
feat/teams-resend-sender

Conversation

@vutuanlinh2k2

Copy link
Copy Markdown
Contributor

What

Adds an opt-in shared helper createResendInvitationSender({ from, apiKey? }): SendInvitationEmailSeam behind a new subpath @tangle-network/agent-app/teams/resend, with resend as an optional peer.

Why

The teams invitations module exposes sendInvitationEmail as a per-app seam (so the core stays transport-agnostic). In practice every Resend-backed adopter hand-writes the same ~15-line wrapper — and both gtm-agent and creative-agent got it wrong the same way: resend.emails.send() returns { data, error } and does not throw on API-level failures (unverified domain, rate limit, bad recipient). A try/catch alone therefore records a failed send as emailStatus: 'sent'. Solving it once removes the silent-failure footgun fleet-wide.

How

src/teams/resend.ts — builds the client lazily (apiKey ?? RESEND_API_KEY; typed failure when neither is set), renders via the existing renderInvitationEmail(input, { fromAddress: from }), sends, and returns { succeeded: false, error } on both a thrown error and a non-null result.error, else { succeeded: true }.

sendInvitationEmail: createResendInvitationSender({ from: GTM_EMAIL_FROM })

resend is an optional peer, imported only in this subpath (same pattern as drizzle-orm/react/konva): apps not on Resend keep passing a raw seam, and the core never pulls a mail dependency. Invite-URL origin + from-address stay app-side (they're already injected params).

Verification

  • New unit test (mock resend): no-key, success, result.error, and thrown-error paths.
  • pnpm vitest run tests/teams — 86 passed. pnpm typecheck — 0 errors. pnpm build emits dist/teams/resend.{js,d.ts}.

Follow-ups (separate, in each app, after publish)

gtm-agent + creative-agent adopt createResendInvitationSender and delete their hand-rolled senders (which fixes creative's identical latent bug).

Adopters back `sendInvitationEmail` with Resend and hand-roll the same wrapper —
and the same bug: `resend.emails.send()` returns `{ data, error }` and does NOT
throw on API-level failures (unverified domain, rate limit), so a try/catch alone
records a failed send as `emailStatus: 'sent'`. Both gtm-agent and creative-agent
carry this latent silent-failure.

Add an opt-in helper behind `@tangle-network/agent-app/teams/resend`:
createResendInvitationSender({ from, apiKey? }) returns a SendInvitationEmailSeam
that builds the client (apiKey ?? RESEND_API_KEY), renders via renderInvitationEmail,
and returns a typed failure on BOTH a thrown error AND a non-null result.error.

`resend` is an OPTIONAL peer (imported only here), so the core stays
provider-agnostic — apps not on Resend keep passing a raw seam.

@tangletools tangletools left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Auto-approved PR — fe20e359

Blanket team auto-approval is enabled for this reviewer service.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: blanket_auto_approve · 2026-06-25T04:14:23Z

@vutuanlinh2k2 vutuanlinh2k2 merged commit 8271654 into main Jun 25, 2026
1 check passed
@vutuanlinh2k2 vutuanlinh2k2 deleted the feat/teams-resend-sender branch June 25, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants