Skip to content

Commit 5dc3fc9

Browse files
committed
Implement Laravel 4.2-inspired mail system with hexagonal DDD architecture
Add complete mail infrastructure with: - mail-core: Domain types, errors, Mailable abstraction, ports (MailDriver, TemplateEngine) - mail-drivers: LogDriver (development) and ArrayDriver (testing) implementations - mail-templates: React Email theme system, pre-built components, and ReactEmailEngine Key features: - Effect-based async operations with typed errors - Branded types for MessageId and EmailAddress - Fluent Mailable API with builder pattern - Theme-based component library (BaseLayout, Button, Alert, Badge, etc.) - Template registry with compile/render support
1 parent 62cf2b2 commit 5dc3fc9

65 files changed

Lines changed: 5827 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/MAIL.md

Lines changed: 1992 additions & 0 deletions
Large diffs are not rendered by default.

libs/mail/core/eslint.config.mjs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import baseConfig from '../../../eslint.config.mjs';
2+
3+
export default [
4+
...baseConfig,
5+
{
6+
files: ['**/*.json'],
7+
rules: {
8+
'@nx/dependency-checks': [
9+
'error',
10+
{
11+
ignoredFiles: [
12+
'{projectRoot}/eslint.config.{js,cjs,mjs}',
13+
'{projectRoot}/esbuild.config.{js,ts,mjs,mts}',
14+
'{projectRoot}/vite.config.{js,ts,mjs,mts}',
15+
],
16+
},
17+
],
18+
},
19+
languageOptions: {
20+
parser: await import('jsonc-eslint-parser'),
21+
},
22+
},
23+
];

libs/mail/core/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@gello/mail-core",
3+
"version": "0.1.0",
4+
"private": true,
5+
"type": "module",
6+
"main": "./src/index.js",
7+
"types": "./src/index.d.ts",
8+
"exports": {
9+
".": {
10+
"types": "./src/index.d.ts",
11+
"import": "./src/index.js"
12+
}
13+
},
14+
"peerDependencies": {
15+
"effect": "^3.0.0"
16+
}
17+
}

libs/mail/core/project.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "mail-core",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/mail/core/src",
5+
"projectType": "library",
6+
"tags": [],
7+
"targets": {
8+
"build": {
9+
"executor": "@nx/esbuild:esbuild",
10+
"outputs": ["{options.outputPath}"],
11+
"options": {
12+
"outputPath": "dist/libs/mail/core",
13+
"main": "libs/mail/core/src/index.ts",
14+
"tsConfig": "libs/mail/core/tsconfig.lib.json",
15+
"assets": [],
16+
"format": ["cjs"],
17+
"generatePackageJson": true
18+
}
19+
}
20+
}
21+
}

libs/mail/core/src/index.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @gello/mail-core
3+
*
4+
* Core domain types, ports, and services for the Gello mail system.
5+
*/
6+
7+
// Domain Types - re-export everything
8+
export * from "./lib/domain/types.js"
9+
10+
// Errors
11+
export {
12+
MailError,
13+
MailConnectionError,
14+
MailValidationError,
15+
MailDeliveryError,
16+
TemplateError,
17+
TemplateNotFoundError,
18+
AttachmentError,
19+
MailConfigError,
20+
type MailSystemError,
21+
} from "./lib/domain/errors.js"
22+
23+
// Mailable
24+
export {
25+
type Mailable,
26+
BaseMailable,
27+
createSimpleMailable,
28+
createTemplateMailable,
29+
type MakeMailableOptions,
30+
} from "./lib/domain/mailable.js"
31+
32+
// Ports
33+
export {
34+
type MailDriver,
35+
MailDriverTag,
36+
} from "./lib/ports/MailDriver.js"
37+
38+
export {
39+
type TemplateEngine,
40+
type RenderedTemplate,
41+
type CompiledTemplate,
42+
TemplateEngineTag,
43+
} from "./lib/ports/TemplateEngine.js"
44+
45+
// Services
46+
export {
47+
type MailService,
48+
MailTag,
49+
MailServiceLive,
50+
} from "./lib/services/Mail.js"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { core } from './core';
2+
3+
describe('core', () => {
4+
it('should work', () => {
5+
expect(core()).toEqual('core');
6+
});
7+
});

libs/mail/core/src/lib/core.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function core(): string {
2+
return 'core';
3+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @gello/mail-core - Error Types
3+
*
4+
* Tagged error types for the mail system.
5+
*/
6+
7+
import { Data } from "effect"
8+
import type { MessageId } from "./types.js"
9+
10+
/**
11+
* Base mail error
12+
*/
13+
export class MailError extends Data.TaggedError("MailError")<{
14+
readonly message: string
15+
readonly cause?: unknown
16+
}> {}
17+
18+
/**
19+
* Connection error to mail provider
20+
*/
21+
export class MailConnectionError extends Data.TaggedError("MailConnectionError")<{
22+
readonly message: string
23+
readonly provider: string
24+
readonly cause?: unknown
25+
}> {}
26+
27+
/**
28+
* Email validation error (invalid address, missing fields, etc.)
29+
*/
30+
export class MailValidationError extends Data.TaggedError("MailValidationError")<{
31+
readonly message: string
32+
readonly field: string
33+
readonly value?: unknown
34+
}> {}
35+
36+
/**
37+
* Delivery error from provider
38+
*/
39+
export class MailDeliveryError extends Data.TaggedError("MailDeliveryError")<{
40+
readonly message: string
41+
readonly messageId: MessageId
42+
readonly provider: string
43+
readonly providerError?: string
44+
readonly statusCode?: number
45+
}> {}
46+
47+
/**
48+
* Template rendering error
49+
*/
50+
export class TemplateError extends Data.TaggedError("TemplateError")<{
51+
readonly message: string
52+
readonly template?: string
53+
readonly cause?: unknown
54+
}> {}
55+
56+
/**
57+
* Template not found error
58+
*/
59+
export class TemplateNotFoundError extends Data.TaggedError("TemplateNotFoundError")<{
60+
readonly message: string
61+
readonly template: string
62+
}> {}
63+
64+
/**
65+
* Attachment error (file not found, too large, etc.)
66+
*/
67+
export class AttachmentError extends Data.TaggedError("AttachmentError")<{
68+
readonly message: string
69+
readonly filename?: string
70+
readonly cause?: unknown
71+
}> {}
72+
73+
/**
74+
* Configuration error
75+
*/
76+
export class MailConfigError extends Data.TaggedError("MailConfigError")<{
77+
readonly message: string
78+
readonly field?: string
79+
}> {}
80+
81+
/**
82+
* Union of all mail-related errors
83+
*/
84+
export type MailSystemError =
85+
| MailError
86+
| MailConnectionError
87+
| MailValidationError
88+
| MailDeliveryError
89+
| TemplateError
90+
| TemplateNotFoundError
91+
| AttachmentError
92+
| MailConfigError

0 commit comments

Comments
 (0)