| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
✅ Rendering Bug Fixes — VerifiedLatest commit (b5cd6e6) fixes all rendering/formatting issues found in review: Fixed Issues
Screenshots (after fixes, with realistic sample data)Event Notice — single clean message, no branch concatenation, no extra HR before first field TestsLint/Type Check |
Sorry, something went wrong.
There was a problem hiding this comment.
This PR replaces the legacy Foundation/Gulp email-template pipeline with a Svelte 5 + Vite + Tailwind-based renderer that generates the static Handlebars HTML consumed by Exceptionless.Core mail delivery.
Changes:
Copilot reviewed 66 out of 68 changed files in this pull request and generated 4 comments.
Show a summary per file| File | Description |
|---|---|
| .gitignore | Adds .gstack/ ignore entry. |
| src/Exceptionless.AppHost/Program.cs | Registers component and email Storybook JavaScript apps. |
| src/Exceptionless.Core/Mail/Templates/event-notice.html | Regenerated event notice email output. |
| src/Exceptionless.Core/Mail/Templates/organization-added.html | Regenerated organization added email output. |
| src/Exceptionless.Core/Mail/Templates/organization-invited.html | Regenerated organization invitation email output. |
| src/Exceptionless.Core/Mail/Templates/organization-notice.html | Regenerated organization limit/throttle notice output. |
| src/Exceptionless.Core/Mail/Templates/organization-payment-failed.html | Regenerated payment failed email output. |
| src/Exceptionless.Core/Mail/Templates/user-email-verify.html | Regenerated email verification output. |
| src/Exceptionless.Core/Mail/Templates/user-password-reset.html | Regenerated password reset output. |
| src/Exceptionless.EmailTemplates/.babelrc | Removes legacy Babel config. |
| src/Exceptionless.EmailTemplates/.gitignore | Updates ignores for the new Svelte/Vite project. |
| src/Exceptionless.EmailTemplates/.npmrc | Removes legacy npm release-age config. |
| src/Exceptionless.EmailTemplates/.prettierignore | Adds formatting ignore paths. |
| src/Exceptionless.EmailTemplates/.prettierrc | Adds Prettier configuration. |
| src/Exceptionless.EmailTemplates/.storybook/main.ts | Configures Storybook for email previews. |
| src/Exceptionless.EmailTemplates/.storybook/preview.ts | Adds Storybook preview parameters. |
| src/Exceptionless.EmailTemplates/LICENSE | Removes legacy ZURB license file. |
| src/Exceptionless.EmailTemplates/README.md | Rewrites documentation for the new Svelte email workflow. |
| src/Exceptionless.EmailTemplates/eslint.config.js | Adds ESLint flat config. |
| src/Exceptionless.EmailTemplates/example.config.json | Removes legacy mail/Litmus example config. |
| src/Exceptionless.EmailTemplates/gulpfile.babel.js | Removes legacy Gulp build pipeline. |
| src/Exceptionless.EmailTemplates/package.json | Replaces legacy Foundation dependencies/scripts with Svelte/Vite/Storybook tooling. |
| src/Exceptionless.EmailTemplates/src/assets/img/.gitkeep | Removes unused legacy asset placeholder. |
| src/Exceptionless.EmailTemplates/src/assets/scss/_settings.scss | Removes legacy Foundation email settings. |
| src/Exceptionless.EmailTemplates/src/assets/scss/app.scss | Removes legacy SCSS entrypoint. |
| src/Exceptionless.EmailTemplates/src/assets/scss/template/_template.scss | Removes legacy email template styling. |
| src/Exceptionless.EmailTemplates/src/build-emails.ts | Adds renderer/cleaner/validator that writes generated HTML templates. |
| src/Exceptionless.EmailTemplates/src/components/ActionsFooter.svelte | Adds shared actions footer component. |
| src/Exceptionless.EmailTemplates/src/components/EmailLayout.svelte | Adds shared email layout/header wrapper. |
| src/Exceptionless.EmailTemplates/src/components/SocialFooter.svelte | Adds shared social/contact footer. |
| src/Exceptionless.EmailTemplates/src/helpers/raw.js | Removes legacy Handlebars raw helper. |
| src/Exceptionless.EmailTemplates/src/layouts/default.html | Removes legacy default layout. |
| src/Exceptionless.EmailTemplates/src/layouts/index-layout.html | Removes legacy index layout. |
| src/Exceptionless.EmailTemplates/src/pages/event-notice.html | Removes legacy event notice source template. |
| src/Exceptionless.EmailTemplates/src/pages/index.html | Removes legacy preview index page. |
| src/Exceptionless.EmailTemplates/src/pages/organization-added.html | Removes legacy organization added source template. |
| src/Exceptionless.EmailTemplates/src/pages/organization-invited.html | Removes legacy organization invited source template. |
| src/Exceptionless.EmailTemplates/src/pages/organization-notice.html | Removes legacy organization notice source template. |
| src/Exceptionless.EmailTemplates/src/pages/organization-payment-failed.html | Removes legacy payment failed source template. |
| src/Exceptionless.EmailTemplates/src/pages/project-daily-summary.html | Removes legacy daily summary source template. |
| src/Exceptionless.EmailTemplates/src/pages/user-email-verify.html | Removes legacy email verify source template. |
| src/Exceptionless.EmailTemplates/src/pages/user-password-reset.html | Removes legacy password reset source template. |
| src/Exceptionless.EmailTemplates/src/partials/social.html | Removes legacy social partial. |
| src/Exceptionless.EmailTemplates/src/stories/EmailPreview.svelte | Adds iframe-based email preview component. |
| src/Exceptionless.EmailTemplates/src/stories/event-notice.stories.svelte | Adds Storybook story for event notice. |
| src/Exceptionless.EmailTemplates/src/stories/organization-added.stories.svelte | Adds Storybook story for organization added. |
| src/Exceptionless.EmailTemplates/src/stories/organization-invited.stories.svelte | Adds Storybook story for organization invited. |
| src/Exceptionless.EmailTemplates/src/stories/organization-notice.stories.svelte | Adds Storybook story for organization notice. |
| src/Exceptionless.EmailTemplates/src/stories/organization-payment-failed.stories.svelte | Adds Storybook story for payment failed. |
| src/Exceptionless.EmailTemplates/src/stories/project-daily-summary.stories.svelte | Adds Storybook story for daily summary. |
| src/Exceptionless.EmailTemplates/src/stories/sample-data.ts | Adds sample token evaluator/data for email previews. |
| src/Exceptionless.EmailTemplates/src/stories/user-email-verify.stories.svelte | Adds Storybook story for email verification. |
| src/Exceptionless.EmailTemplates/src/stories/user-password-reset.stories.svelte | Adds Storybook story for password reset. |
| src/Exceptionless.EmailTemplates/src/templates/event-notice.svelte | Adds Svelte source for event notice email. |
| src/Exceptionless.EmailTemplates/src/templates/organization-added.svelte | Adds Svelte source for organization added email. |
| src/Exceptionless.EmailTemplates/src/templates/organization-invited.svelte | Adds Svelte source for organization invitation email. |
| src/Exceptionless.EmailTemplates/src/templates/organization-notice.svelte | Adds Svelte source for organization notice email. |
| src/Exceptionless.EmailTemplates/src/templates/organization-payment-failed.svelte | Adds Svelte source for payment failed email. |
| src/Exceptionless.EmailTemplates/src/templates/project-daily-summary.svelte | Adds Svelte source for daily summary email. |
| src/Exceptionless.EmailTemplates/src/templates/user-email-verify.svelte | Adds Svelte source for email verification email. |
| src/Exceptionless.EmailTemplates/src/templates/user-password-reset.svelte | Adds Svelte source for password reset email. |
| src/Exceptionless.EmailTemplates/src/theme.ts | Adds centralized email color/theme tokens. |
| src/Exceptionless.EmailTemplates/svelte.config.js | Adds Svelte preprocessing config. |
| src/Exceptionless.EmailTemplates/tsconfig.json | Adds TypeScript config for the email-template project. |
| src/Exceptionless.EmailTemplates/vite.config.ts | Adds Vite SSR build config. |
| src/Exceptionless.Web/ClientApp/package.json | Prevents ClientApp Storybook from auto-opening a browser. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Sorry, something went wrong.
Dogfood Evidence — Mailpit Email RendersAll 8 templates sent to Mailpit via SMTP and visually verified. HTML Check scores range 87–94%. Bug Found & Fixed: IsRegressed badge was never renderingRoot cause: GetStackTemplateData() in Mailer.cs returned the stack data without IsRegressed, so {{#if IsRegressed}}[REGRESSED]{{/if}} always evaluated false. The badge silently never appeared for regressed stacks — in main or in this PR until caught. Fix: Added IsRegressed = s.Status == StackStatus.Regressed to GetStackTemplateData(). Proof it works (visible in daily summary email — see screenshot): the [REGRESSED] prefix appears on ArgumentNullException in the Most Frequent list. Coverage Gap FixedRunMailJobAsync() in MailerTests.cs only logged the rendered body — zero assertions. All 25 tests would pass even if templates rendered empty HTML or left {{token}} placeholders unsubstituted. Fix:
dotnet test -- --filter-class Exceptionless.Tests.Mail.MailerTests # total: 26 | failed: 0 | succeeded: 26 Email Renders (new Svelte 5 + Tailwind templates)Verified via Mailpit at http://localhost:8025 after npm run build in src/Exceptionless.EmailTemplates:
|
Sorry, something went wrong.
📸 Visual Comparison: All 8 Templates (Before → After)All templates rendered in Mailpit with identical sample data. Left = main branch (Foundation/Inky), Right = PR (Svelte 5 + Tailwind). Email VerifyPassword ResetEvent NoticeDaily Summary
Organization AddedOrganization InvitedOrganization Notice (Throttled)Payment FailedVerification: All 16 emails (8 OLD + 8 NEW) successfully delivered to Mailpit SMTP. All 26 mailer tests pass. 🟢 |
Sorry, something went wrong.
Replace the legacy Foundation for Emails (Gulp/Inky/Panini/SCSS) toolchain with Svelte 5 + @better-svelte-email + Tailwind CSS. - Migrate all 8 email templates to Svelte components - Add shared EmailLayout, ActionsFooter, SocialFooter components - New build system: Vite SSR + @better-svelte-email/server renderer - Output maintains identical visual appearance and Handlebars tokens - All 25 mailer tests pass with new template output - Remove old build tooling (Gulp, Babel, SCSS, Panini, Inky) The compiled HTML templates preserve Handlebars syntax for runtime rendering by HandlebarsDotNet in the .NET backend (unchanged). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Migrate 8 email templates from Foundation for Emails / Gulp / Inky / Panini / SCSS
to @better-svelte-email/server 2.1.1 + Tailwind CSS
- Security audit: Svelte upgraded 5.34.7 → 5.55.9 (patched 6 XSS SSR CVEs)
- Pixel-perfect visual parity: all 8 templates verified with before/after screenshots
- Centralized design tokens in src/theme.ts (named Tailwind colors: text-primary,
bg-dark, text-alert, etc.) — no more hardcoded hex in .svelte sources
- Fixed XSS: preheader was {@html preheader}, now plain {preheader} text binding
- Add Storybook 10 with stories for all 8 templates + sample data with fillTokens()
- Add ESLint (flat config), Prettier, svelte-check — 0 errors, 25/25 tests pass
- Add @types/node, vite/client types, skipLibCheck for clean type checking
- Fix build script: typed Component, parseInt radix, HTML comment stripping
- Fix JSON-LD '}\n}' → '}}' Handlebars parse collision in cleanHtml
- Remove compilerOptions.generate from vite.config.ts (Svelte 5 no longer supports it)
- Remove old Gulp/Babel/SCSS/Panini/Inky/Foundation toolchain entirely
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rewrite fillTokens in sample-data.ts as a proper Handlebars evaluator
supporting if/else/each blocks, nested depth tracking, @index and
{{../parent}} scope resolution — fixes token concatenation bug where all
{{#if}} branches were showing simultaneously
- Fix cleanHtml() to replace newlines in text nodes with a space instead of
removing them (was causing 'fromwhich', 'yourapplication', etc.)
- Fix text typos in organization-notice.svelte: 'to to continue' → 'to continue',
'to to see' → 'to see', 'being counting' → 'counting'
- Add Storybook (port 6006) and EmailStorybook (port 6008) as AddJavaScriptApp
resources in Aspire AppHost for integrated development dashboard
- Change email Storybook port to 6008 to avoid conflict with Svelte app (6006)
- Rebuild all 8 generated HTML templates with whitespace fixes applied
- 25/25 mailer tests pass
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- vite.config.ts: add closeBundle plugin so 'dev' (--watch) regenerates
HTML templates on every source change; simplify 'build' script to
'vite build' (plugin handles node dist/build.js). Remove try/catch so
renderer failures propagate and fail the build.
- sample-data.ts: fix isTruthy to match Handlebars semantics exactly —
store @index as a real number so {{#if @index}} is falsy at index 0
(matches HandlebarsDotNet integer semantics); strings 'false'/'null'/
'undefined'/'' are falsy, all other strings truthy (matching
Handlebars.js, not JS). Change BASE_URL to http://localhost:7110.
- dependabot.yml: add npm entry for /src/Exceptionless.EmailTemplates
- build.yaml: add test-email-templates CI job that runs lint, check,
build, and verifies generated HTML is committed and up-to-date
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add IsRegressed to GetStackTemplateData in Mailer.cs
- Fix project-daily-summary: wrap all MostFrequent/Newest items in single <ul>
- Refactor SocialFooter to use Row+Column components (remove {#@html} hack)
- Use Preview component in EmailLayout; remove unconditional spacer div
- Replace 200-line custom Handlebars evaluator with handlebars npm package
- Move JSON-LD to wrapJsonLd() helper in src/lib/json-ld.ts (avoids Svelte
parser treating <script> in template literals as real script elements)
- Include src/templates/** in ESLint with no-at-html-tags rule disabled
- Remove .gstack/ from repo-root .gitignore
- Remove obvious code comments from build-emails.ts
- Fix no-unused-vars: remove Link import from user-email-verify.svelte
- Regenerate all 8 HTML templates
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fields is iterated with {{#each Fields}} where @key = property name
and this = value. Using a plain object (not array of objects) matches
HandlebarsDotNet's Dictionary<string,string> iteration semantics and
produces correct key/value pairs in the Storybook preview.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ressed badge - RunMailJobAsync() now returns string body for assertions - All 25 tests assert template-specific content (buttons, headings, key phrases) - New test SendProjectDailySummaryWithRegressedStackAsync verifies [REGRESSED] badge appears when StackStatus.Regressed - this was the bug: IsRegressed was missing from GetStackTemplateData so the badge never rendered - Total: 26 tests passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lpit Side-by-side comparisons of all 8 email templates rendered in Mailpit. BEFORE = main branch Foundation/Inky HTML, AFTER = PR Svelte 5 + Tailwind. Both rendered with identical sample data to verify pixel-accurate parity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…daily summary Use @better-svelte-email/components Row and Column instead of raw HTML strings for the 3-column and 4-column stats tables in project-daily-summary.svelte. Also refactor the throttling text to use a single @html block instead of mixing @html with Svelte Link components. All 26 mailer tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nodemailer was installed locally for email-to-Mailpit testing only. It's not needed as a production dependency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# Conflicts: # src/Exceptionless.AppHost/Extensions/WorktreeScope.cs # src/Exceptionless.AppHost/Program.cs
|
Follow-up reviewer audit complete at 270cbfa974777fb3b6a80c23732bb5ca050d4419. I re-read all live review threads, inline comments, submitted reviews, and issue comments against the current branch-vs-main diff. The 18 review threads classify as 14 outdated/already-fixed or superseded-by-the-current implementation and 4 current/already-addressed. One previously resolved human finding had regressed: .gstack/ was removed in response to review, then reintroduced later by 07dbfb878. I removed that unrelated ignore entry again and replied on the original thread with the commit-level RCA and proof. The thermo-nuclear pass found one additional real boundary gap: parity validation compared visible text and links but did not inspect Gmail application/ld+json actions, so structured-data actions could drift without failing the gate. The validator now parses and exact-compares every JSON-LD document between legacy and modern output. I also removed a redundant CI build step because validate:parity already performs the build. Verification:
No remaining actionable review concern or external blocker was found. The complete structural pass found no >1,000-line source file, unsafe boundary/type leak, duplicated canonical helper, non-atomic update, or clear behavior-preserving broad refactor beyond the focused CI simplification above. |
Sorry, something went wrong.
|
Addressed the invitation feedback and audited every internal email URL against the Svelte UI. The organization invitation does not provision an account or password-reset token, so a setup-password page would be the wrong contract. A genuinely new address now follows the invitation email directly to /signup?token=...; if that address has acquired an account before the invitation is accepted, /login?token=... now preserves the token through password and OAuth login and sends it to the API so organization membership is completed. Direct organization links also retain the organization from the URL. I migrated the remaining legacy email routes to their Svelte equivalents and extended parity validation to discover SvelteKit routes and verify both clean URLs and the current /next deployment prefix. Verification at 73b07ce9b:
Dependencies were updated to the latest compatible releases; remaining outdated entries are major-version upgrades and were intentionally left for separate compatibility work. |
Sorry, something went wrong.
|
Replacement CI is fully green at 73b07ce9b: API/coverage, client, email templates, full E2E, container build, version, and CLA all passed. Run: https://github.com/exceptionless/Exceptionless/actions/runs/30580372048 |
Sorry, something went wrong.
Refresh the EmailTemplates Svelte/Vite/Storybook toolchain and lockfile while preserving generated output.
…ates-modernization
…o-review-20260809
This reverts commit b800dd6.
| const response = await client.postJSON<TokenResult>(`auth/${options.provider}`, { | ||
| clientId: options.clientId, | ||
| code: data.code, | ||
| inviteToken: options.inviteToken, |
There was a problem hiding this comment.
[P1] Honor invitation tokens before blocking OAuth account creation
This new client path forwards the token, but a brand-new OAuth identity still cannot use it when account creation is disabled. ExternalLoginAsync calls FromExternalLoginAsync, whose user is null branch checks only authOptions.EnableAccountCreation; it returns 403 before AddInvitedUserToOrganizationAsync can consume authInfo.InviteToken. Password signup already gates through IsAccountCreationEnabledAsync(model.InviteToken), so invited users see OAuth signup buttons that fail in the deployment mode invitations are specifically meant to support. Please make the external-login creation gate token-aware and add an AuthEndpointTests case for disabled account creation + a valid invite + a new provider user.
Sorry, something went wrong.
| const __dirname = dirname(__filename); | ||
|
|
||
| const templates: Record<string, Component> = { | ||
| 'user-password-reset': UserPasswordReset as unknown as Component, |
There was a problem hiding this comment.
[P2] Keep the registry’s actual component types
Every template is erased through unknown just to satisfy this Record<string, Component> annotation. That disables checking at the canonical registry boundary and repeats the same cast eight times, while Renderer.render accepts the imported component values directly. Please let the object infer its value types (and remove the Component import/casts), or introduce a real renderable-component contract if the renderer gains one, so an accidental non-component entry is caught rather than asserted away.
Sorry, something went wrong.
# Conflicts: # src/Exceptionless.Web/ClientApp/src/lib/features/auth/api.svelte.ts # src/Exceptionless.Web/ClientApp/src/lib/features/auth/index.svelte.ts
|
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Summary
Modernizes the 8 Exceptionless email templates from a decade-old Foundation for Emails / Gulp / Inky / Panini / SCSS toolchain to Svelte 5 + @better-svelte-email + Tailwind CSS, with zero visual regressions, Storybook for live preview, and a comprehensive security audit.
What Changed
Architecture
Security Fixes
Security audit result for @better-svelte-email: SAFE TO ADOPT. Clean dependency tree (parse5, postcss, tailwindcss). SLSA provenance verified. Single-maintainer bus factor noted; versions pinned exactly.
Code Quality (Staff Engineer Review Fixes)
Bug Fixes
1. IsRegressed badge silently broken (fixed in this PR)
GetStackTemplateData in Mailer.cs never included IsRegressed in the data dictionary, so {{#if IsRegressed}}[REGRESSED]{{/if}} in templates always evaluated false — the badge never appeared for regressed stacks. This PR adds IsRegressed = s.Status == StackStatus.Regressed to fix it.
2. Zero body assertions in mailer tests (coverage gap fixed)
RunMailJobAsync() only logged the rendered body — zero assertions. Broken template rendering would pass silently. Fixed by:
3. JSON-LD whitespace collapse bug
cleanHtml's whitespace collapsing would turn JSON-LD }\n} → }}, which HandlebarsDotNet parsed as a Handlebars closing token. Fix: extract <script type="application/ld+json"> blocks before whitespace collapse, restore with newlines after.
New Tooling
Visual Parity
All 8 templates verified via Mailpit SMTP delivery + browser screenshots. HTML Check scores: 87–94%.
See PR comment below for Mailpit screenshots of all 8 rendered emails.
How to Run
Tests
All 26 mailer tests pass (25 existing + 1 new IsRegressed test):
Breaking Changes
None. C# Mailer.cs and all Handlebars templates are fully backward-compatible. The compiled .html output format is identical to the old toolchain.
Final production-readiness verification (2026-07-12)