[local-explorer-ui] Add email view and sending emails to workers to the local explorer - #15337
[local-explorer-ui] Add email view and sending emails to workers to the local explorer#15337tpmmorris wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: 2e5762f The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/codemods
@cloudflare/config
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-plugin
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
There was a problem hiding this comment.
Pull request overview
Adds Local Explorer email inspection, routing, and test-email sending across the Miniflare API and UI.
Changes:
- Extends email APIs, contracts, schemas, MIME handling, and validation.
- Adds routing and sending views, composition, previews, pagination, and worker navigation.
- Adds unit, API, and E2E tests plus release metadata.
Reviewed changes
Copilot reviewed 38 out of 41 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Summary |
|---|---|
packages/miniflare/test/plugins/local-explorer/email.spec.ts |
Email API coverage |
packages/miniflare/src/workers/local-explorer/resources/email.ts |
Email parsing and validation |
packages/miniflare/src/workers/local-explorer/openapi.local.json |
API schema updates |
packages/miniflare/src/workers/local-explorer/generated/zod.gen.ts |
Generated validation schemas |
packages/miniflare/src/workers/local-explorer/generated/types.gen.ts |
Generated API types |
packages/miniflare/src/workers/email/mime.ts |
MIME header handling |
packages/miniflare/src/workers/email/contracts.ts |
Email API contracts |
packages/local-explorer-ui/vite.config.ts |
Route generation configuration |
packages/local-explorer-ui/tsconfig.e2e.json |
E2E type inclusion |
packages/local-explorer-ui/src/utils/worker-navigation.ts |
Worker-switch navigation |
packages/local-explorer-ui/src/utils/sidebar-state.ts |
Email sidebar state |
packages/local-explorer-ui/src/utils/email-html.ts |
Safe HTML previews |
packages/local-explorer-ui/src/routeTree.gen.ts |
Generated email routes |
packages/local-explorer-ui/src/routes/email/shared/useCursorPaginatedList.ts |
Cursor pagination. Moderate (2 votes): stale request rejections can update the error after a worker switch. |
packages/local-explorer-ui/src/routes/email/shared/types.ts |
Shared email types |
packages/local-explorer-ui/src/routes/email/shared/InfoLoading.tsx |
Loading state |
packages/local-explorer-ui/src/routes/email/shared/InfoFlow.tsx |
Email event flow |
packages/local-explorer-ui/src/routes/email/shared/format.ts |
Email formatting |
packages/local-explorer-ui/src/routes/email/shared/EventNode.tsx |
Event timeline nodes |
packages/local-explorer-ui/src/routes/email/shared/ConstantsCard.tsx |
Event metadata display |
packages/local-explorer-ui/src/routes/email/sending.tsx |
Sending route |
packages/local-explorer-ui/src/routes/email/routing/index.tsx |
Routing list route |
packages/local-explorer-ui/src/routes/email/routing/$emailId.tsx |
Routing detail route |
packages/local-explorer-ui/src/routes/email/routing.tsx |
Routing layout |
packages/local-explorer-ui/src/routes/__root.tsx |
Root navigation integration |
packages/local-explorer-ui/src/components/Sidebar.tsx |
Email sidebar entry |
packages/local-explorer-ui/src/components/email/SentEmailDetails.tsx |
Sent-email details |
packages/local-explorer-ui/src/components/email/SendTestEmailDialog.tsx |
Test-email composer |
packages/local-explorer-ui/src/components/email/ReceivedEmailContent.tsx |
Received-email content |
packages/local-explorer-ui/src/components/email/EmailTruncationWarning.tsx |
Truncation warning |
packages/local-explorer-ui/src/components/email/EmailServiceEmptyState.tsx |
Empty state |
packages/local-explorer-ui/src/components/email/EmailPagination.tsx |
Pagination controls |
packages/local-explorer-ui/src/components/email/EmailList.tsx |
Email list rendering |
packages/local-explorer-ui/src/components/email/EmailHtmlPreview.tsx |
HTML preview |
packages/local-explorer-ui/src/assets/icons/email.svg |
Email icon |
packages/local-explorer-ui/src/__tests__/utils/worker-navigation.test.ts |
Navigation tests |
packages/local-explorer-ui/src/__tests__/utils/sidebar-state.test.ts |
Sidebar tests |
packages/local-explorer-ui/src/__tests__/utils/email-html.test.ts |
HTML preview tests |
packages/local-explorer-ui/src/__e2e__/worker-selector.spec.ts |
Worker-selection E2E tests. Critical (3 votes): locators at lines 384, 443, and 447 use a link role although email rows render as buttons, so the test fails before exercising worker switching. |
packages/local-explorer-ui/src/__e2e__/email-sending.spec.ts |
Email-sending E2E tests |
.changeset/email-local-explorer-ui.md |
Release metadata |
Suppressed comments (8)
packages/local-explorer-ui/src/e2e/worker-selector.spec.ts:443
- The email rows are rendered as buttons, so this link-role locator cannot find
Worker two emailand the test will fail after the worker switch. Use the button role to matchEmailList.
await page.getByRole("link", { name: /Worker two email/ }).waitFor();
packages/local-explorer-ui/src/e2e/worker-selector.spec.ts:447
- This is the same row-role mismatch as above:
EmailListuses buttons, so the assertion queries no email rows and will not verify that the old row disappeared. Query the button role instead.
expect(await page.getByRole("link", { name: /Old email/ }).count()).toBe(0);
packages/local-explorer-ui/src/components/email/EmailList.tsx:65
- The refresh control remains enabled while
disabledis true for pagination. Clicking Refresh during an in-flight next/previous request starts a secondloadPage; the responses can then leave the displayed items and cursor history out of sync. Disable this button with the samedisabledstate (or otherwise serialize the operations).
disabled={refreshing}
packages/local-explorer-ui/src/components/email/SendTestEmailDialog.tsx:73
- The parser populates this ordinary object through dynamic keys. A valid HTTP field name such as
__proto__invokes Object.prototype's setter on{}rather than creating an enumerable header, so that custom header is silently omitted from the request. Use a null-prototype record or collect entries withObject.fromEntries.
const headers: Record<string, string> = {};
packages/local-explorer-ui/src/components/email/SendTestEmailDialog.tsx:196
- This dialog-wide error is cleared whenever an attachment finishes reading. That hides unrelated validation errors (for example, a missing sender) and can also erase the capacity error from a concurrent rejected selection, even though the form is still invalid or the file was not added. Clear attachment errors when starting an accepted selection, or keep attachment and form errors separate.
setError(null);
packages/local-explorer-ui/src/components/email/SendTestEmailDialog.tsx:175
- The limit check allows a total exactly equal to
MAX_TOTAL_ATTACHMENT_BYTES(>rejects only values above it), but this message says the total must be “less than” the limit. Use wording such as “at most” so the boundary behavior is accurately communicated.
`Attachments must total less than ${formatSize(MAX_TOTAL_ATTACHMENT_BYTES)}.`
packages/local-explorer-ui/src/components/email/SentEmailDetails.tsx:169
- A capture can set
truncatedwhile omitting bothtextandhtmlwhen even their empty representations do not fit in the row;rawcan be absent in the same situation. This branch ignorestruncatedand instead reports that the email has no captured body, hiding the important truncation warning. RenderEmailTruncationWarningwhentruncatedis true here as well.
{!email.text && !email.html && !email.raw ? (
<div className="rounded-lg border border-kumo-fill bg-kumo-elevated px-5 py-8 text-center text-sm text-kumo-subtle">
This email has no captured text or HTML body.
</div>
) : null}
packages/local-explorer-ui/src/routes/email/routing/$emailId.tsx:46
- The API emits a separate warning when a captured reply is truncated, but this loader only checks for the primary received-email warning. As a result,
EventNodecan render a partialreply.rawwithout any warning. Propagate the reply-truncation state to the affected reply event and show the warning there without marking the complete primary message as truncated.
const truncated = hasEmailTruncationWarning(
response.data?.messages ?? [],
"received"
);
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| viteUrl | ||
| ).toString() | ||
| ); | ||
| await page.getByRole("link", { name: /Old email/ }).waitFor(); |
| const page = await fetchPage(cursor); | ||
| if (requestId !== request.current) { | ||
| return false; | ||
| } | ||
| setItems(page.items); | ||
| setNextCursor(page.nextCursor); | ||
| return true; |
b4664ef to
b3a90b7
Compare
b3a90b7 to
2e5762f
Compare
|
Misc feedback, not necessarily all for this PR Email routing:
email sending:
both:
also maybe worth throwing https://kumo-ui.com/skill.md into an llm for a review |
Fixes #13648
Adds email inspection and sending of test emails to workers to the local explorer.
The new email group has two tabs - routing and sending - which provide users with inboxes of emails sent from/to workers respectively. Within these views, users can open detail views to see message content, and for received emails the handling path.
Emails can be composed and delivered to workers, which provides access to standard fields, as well as custom headers, html, and attachments.