refactor(domain): add FeedId, EmailAddress and Domain value objects

Encapsulate the email/domain/feed-id parsing that was scattered as ad-hoc
regexes and split("@") calls into three small immutable value objects under
src/domain/value-objects/. EmailParser.extractFeedId and generateFeedId now
delegate to FeedId; the sender policy, favicon domain extraction and the admin
SenderField parse through EmailAddress/Domain.

Left as-is on purpose: forwardemail's multi-address free-text extraction and the
admin allow/block list normaliser, which operate on mixed email-or-domain input
that the single-address value objects would reject.

Behaviour-preserving; adds unit tests for each value object.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
Julien HerrandClaude Opus 4.7 committed 2026-05-24 00:05:46 +02:00
1 parent 8f036cf223
commit c65aabe7f4
11 files changed
+198 -35

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
import { describe, it, expect } from "vitest";
import { Domain } from "./domain";
describe("Domain", () => {
it("normalises case and whitespace", () => {
expect(Domain.parse(" Example.COM ")?.value).toBe("example.com");
});
it("strips a leading @ and trailing dots", () => {
expect(Domain.parse("@example.com")?.value).toBe("example.com");
expect(Domain.parse("example.com.")?.value).toBe("example.com");
});
it("returns null for empty input", () => {
expect(Domain.parse("")).toBeNull();
expect(Domain.parse("@")).toBeNull();
});
it("compares by normalised value", () => {
expect(
Domain.parse("Example.com")!.matches(Domain.parse("example.com")!),
).toBe(true);
expect(Domain.parse("a.com")!.matches(Domain.parse("b.com")!)).toBe(false);
});
});