alepha 0.14.4 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -102
- package/dist/api/audits/index.d.ts +331 -443
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +2 -2
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +0 -113
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +2 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +151 -262
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/notifications/index.browser.js +4 -4
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +164 -276
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +4 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +265 -377
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.browser.js +1 -2
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +195 -301
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +203 -184
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +1 -2
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +2 -2
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +5900 -165
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1481 -639
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +8 -4
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +29 -25
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +563 -54
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +175 -8
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +564 -54
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +563 -54
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +4 -4
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +89 -42
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +129 -33
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +7969 -2
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +22 -22
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +134 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +253 -1
- package/dist/file/index.js.map +1 -1
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/logger/index.d.ts +1 -2
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +1 -5
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +19 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +28 -4
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/chunk-DH6iiROE.js +38 -0
- package/dist/orm/index.browser.js +9 -9
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +2821 -0
- package/dist/orm/index.bun.js.map +1 -0
- package/dist/orm/index.d.ts +318 -169
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +2086 -1776
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/redis/index.bun.js +285 -0
- package/dist/redis/index.bun.js.map +1 -0
- package/dist/redis/index.d.ts +13 -31
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +18 -38
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts +83 -1
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +393 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.browser.js +5 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +598 -112
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1808 -97
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +1200 -175
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1268 -37
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +6 -3
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +1 -1
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +3 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +115 -13
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +321 -139
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +0 -1
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/health/index.d.ts +0 -1
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/links/index.browser.js +9 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +1 -2
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +14 -7
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +514 -1
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +4462 -4
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/proxy/index.d.ts +0 -1
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/swagger/index.d.ts +1 -2
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1 -2
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -1
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +10 -10
- package/dist/sms/index.js.map +1 -1
- package/dist/thread/index.d.ts +0 -1
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +2 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/vite/index.d.ts +6315 -149
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +140 -469
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +9 -9
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +28 -28
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +9 -9
- package/dist/websocket/index.js.map +1 -1
- package/package.json +13 -18
- package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
- package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
- package/src/api/users/entities/users.ts +1 -1
- package/src/api/users/index.ts +8 -8
- package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
- package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
- package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
- package/src/api/users/services/CredentialService.ts +7 -7
- package/src/api/users/services/IdentityService.ts +4 -4
- package/src/api/users/services/RegistrationService.spec.ts +25 -27
- package/src/api/users/services/RegistrationService.ts +38 -27
- package/src/api/users/services/SessionCrudService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +3 -3
- package/src/api/users/services/SessionService.ts +27 -18
- package/src/api/users/services/UserService.ts +7 -7
- package/src/batch/providers/BatchProvider.ts +1 -2
- package/src/cli/apps/AlephaCli.ts +2 -2
- package/src/cli/apps/AlephaPackageBuilderCli.ts +47 -20
- package/src/cli/assets/apiHelloControllerTs.ts +19 -0
- package/src/cli/assets/apiIndexTs.ts +16 -0
- package/src/cli/assets/biomeJson.ts +2 -1
- package/src/cli/assets/claudeMd.ts +308 -0
- package/src/cli/assets/dummySpecTs.ts +2 -1
- package/src/cli/assets/editorconfig.ts +2 -1
- package/src/cli/assets/mainBrowserTs.ts +4 -3
- package/src/cli/assets/mainCss.ts +24 -0
- package/src/cli/assets/mainServerTs.ts +24 -0
- package/src/cli/assets/tsconfigJson.ts +2 -1
- package/src/cli/assets/webAppRouterTs.ts +16 -0
- package/src/cli/assets/webHelloComponentTsx.ts +20 -0
- package/src/cli/assets/webIndexTs.ts +16 -0
- package/src/cli/atoms/appEntryOptions.ts +13 -0
- package/src/cli/atoms/buildOptions.ts +1 -1
- package/src/cli/atoms/changelogOptions.ts +1 -1
- package/src/cli/commands/build.ts +97 -61
- package/src/cli/commands/db.ts +21 -18
- package/src/cli/commands/deploy.ts +17 -5
- package/src/cli/commands/dev.ts +26 -47
- package/src/cli/commands/gen/env.ts +1 -1
- package/src/cli/commands/init.ts +79 -25
- package/src/cli/commands/lint.ts +9 -3
- package/src/cli/commands/test.ts +8 -2
- package/src/cli/commands/typecheck.ts +5 -1
- package/src/cli/commands/verify.ts +4 -2
- package/src/cli/defineConfig.ts +9 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/providers/AppEntryProvider.ts +131 -0
- package/src/cli/providers/ViteBuildProvider.ts +82 -0
- package/src/cli/providers/ViteDevServerProvider.ts +350 -0
- package/src/cli/providers/ViteTemplateProvider.ts +27 -0
- package/src/cli/services/AlephaCliUtils.ts +72 -602
- package/src/cli/services/PackageManagerUtils.ts +308 -0
- package/src/cli/services/ProjectScaffolder.ts +329 -0
- package/src/command/helpers/Runner.ts +15 -3
- package/src/core/Alepha.ts +2 -8
- package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +2 -0
- package/src/core/primitives/$hook.ts +6 -2
- package/src/core/primitives/$module.spec.ts +4 -0
- package/src/core/primitives/$module.ts +12 -0
- package/src/core/providers/AlsProvider.ts +1 -1
- package/src/core/providers/CodecManager.spec.ts +12 -6
- package/src/core/providers/CodecManager.ts +26 -6
- package/src/core/providers/EventManager.ts +169 -13
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +878 -0
- package/src/core/providers/KeylessJsonSchemaCodec.ts +789 -0
- package/src/core/providers/SchemaValidator.spec.ts +236 -0
- package/src/core/providers/StateManager.spec.ts +27 -16
- package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
- package/src/email/providers/LocalEmailProvider.ts +52 -15
- package/src/email/providers/NodemailerEmailProvider.ts +167 -56
- package/src/file/errors/FileError.ts +7 -0
- package/src/file/index.ts +9 -1
- package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
- package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
- package/src/mcp/errors/McpError.ts +30 -0
- package/src/mcp/index.ts +3 -0
- package/src/mcp/transports/SseMcpTransport.ts +16 -6
- package/src/orm/index.browser.ts +1 -19
- package/src/orm/index.bun.ts +77 -0
- package/src/orm/index.shared-server.ts +22 -0
- package/src/orm/index.shared.ts +15 -0
- package/src/orm/index.ts +19 -39
- package/src/orm/providers/DrizzleKitProvider.ts +3 -5
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
- package/src/orm/services/Repository.ts +19 -0
- package/src/redis/index.bun.ts +35 -0
- package/src/redis/providers/BunRedisProvider.ts +12 -43
- package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
- package/src/redis/providers/NodeRedisProvider.ts +16 -34
- package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
- package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
- package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
- package/src/security/index.browser.ts +5 -0
- package/src/security/index.ts +90 -7
- package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
- package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
- package/src/security/primitives/$role.ts +5 -5
- package/src/security/primitives/$serviceAccount.spec.ts +5 -5
- package/src/security/primitives/$serviceAccount.ts +3 -3
- package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
- package/src/server/auth/primitives/$auth.ts +10 -10
- package/src/server/auth/primitives/$authCredentials.ts +3 -3
- package/src/server/auth/primitives/$authGithub.ts +3 -3
- package/src/server/auth/primitives/$authGoogle.ts +3 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
- package/src/server/cache/providers/ServerCacheProvider.ts +1 -1
- package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
- package/src/server/core/index.ts +1 -1
- package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
- package/src/server/core/providers/NodeHttpServerProvider.ts +92 -24
- package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
- package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
- package/src/server/core/providers/ServerProvider.ts +144 -24
- package/src/server/core/providers/ServerRouterProvider.ts +259 -115
- package/src/server/core/providers/ServerTimingProvider.ts +2 -2
- package/src/server/links/atoms/apiLinksAtom.ts +7 -0
- package/src/server/links/index.browser.ts +2 -0
- package/src/server/links/index.ts +3 -1
- package/src/server/links/providers/LinkProvider.ts +1 -1
- package/src/server/swagger/index.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
- package/src/sms/providers/LocalSmsProvider.ts +8 -7
- package/src/vite/index.ts +3 -2
- package/src/vite/tasks/buildClient.ts +0 -1
- package/src/vite/tasks/buildServer.ts +80 -22
- package/src/vite/tasks/copyAssets.ts +5 -4
- package/src/vite/tasks/generateCloudflare.ts +7 -0
- package/src/vite/tasks/generateSitemap.ts +64 -23
- package/src/vite/tasks/index.ts +0 -2
- package/src/vite/tasks/prerenderPages.ts +49 -24
- package/dist/server/security/index.browser.js +0 -13
- package/dist/server/security/index.browser.js.map +0 -1
- package/dist/server/security/index.d.ts +0 -173
- package/dist/server/security/index.d.ts.map +0 -1
- package/dist/server/security/index.js +0 -311
- package/dist/server/security/index.js.map +0 -1
- package/src/cli/assets/appRouterTs.ts +0 -9
- package/src/cli/assets/indexHtml.ts +0 -15
- package/src/cli/assets/mainTs.ts +0 -13
- package/src/cli/commands/format.ts +0 -17
- package/src/server/security/index.browser.ts +0 -10
- package/src/server/security/index.ts +0 -94
- package/src/vite/helpers/boot.ts +0 -106
- package/src/vite/plugins/viteAlephaDev.ts +0 -177
- package/src/vite/tasks/devServer.ts +0 -69
- package/src/vite/tasks/runAlepha.ts +0 -270
- /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
- /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
|
@@ -1,24 +1,64 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { $atom, $hook, $inject, $use, type Static, t } from "alepha";
|
|
2
|
+
import { FileSystemProvider } from "alepha/file";
|
|
3
3
|
import { $logger } from "alepha/logger";
|
|
4
4
|
import { EmailError } from "../errors/EmailError.ts";
|
|
5
5
|
import type { EmailProvider, EmailSendOptions } from "./EmailProvider.ts";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Local email provider configuration atom
|
|
11
|
+
*/
|
|
12
|
+
export const localEmailOptions = $atom({
|
|
13
|
+
name: "alepha.email.local.options",
|
|
14
|
+
schema: t.object({
|
|
15
|
+
directory: t.string({
|
|
16
|
+
description: "Directory path where email files will be stored",
|
|
17
|
+
}),
|
|
18
|
+
}),
|
|
19
|
+
default: {
|
|
20
|
+
directory: "node_modules/.alepha/emails",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type LocalEmailProviderOptions = Static<typeof localEmailOptions.schema>;
|
|
25
|
+
|
|
26
|
+
declare module "alepha" {
|
|
27
|
+
interface State {
|
|
28
|
+
[localEmailOptions.key]: LocalEmailProviderOptions;
|
|
29
|
+
}
|
|
12
30
|
}
|
|
13
31
|
|
|
32
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
33
|
+
|
|
14
34
|
export class LocalEmailProvider implements EmailProvider {
|
|
15
35
|
protected readonly log = $logger();
|
|
16
|
-
protected readonly
|
|
36
|
+
protected readonly fs = $inject(FileSystemProvider);
|
|
37
|
+
protected readonly options = $use(localEmailOptions);
|
|
17
38
|
|
|
18
|
-
|
|
19
|
-
this.
|
|
39
|
+
protected get directory(): string {
|
|
40
|
+
return this.options.directory;
|
|
20
41
|
}
|
|
21
42
|
|
|
43
|
+
protected onStart = $hook({
|
|
44
|
+
on: "start",
|
|
45
|
+
handler: async () => {
|
|
46
|
+
try {
|
|
47
|
+
await this.fs.mkdir(this.directory, { recursive: true });
|
|
48
|
+
this.log.info("Email directory OK", {
|
|
49
|
+
directory: this.directory,
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
const message = `Failed to create email directory: ${error instanceof Error ? error.message : String(error)}`;
|
|
53
|
+
this.log.error(message, { directory: this.directory });
|
|
54
|
+
throw new EmailError(
|
|
55
|
+
message,
|
|
56
|
+
error instanceof Error ? error : undefined,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
22
62
|
public async send(options: EmailSendOptions): Promise<void> {
|
|
23
63
|
const { to, subject, body } = options;
|
|
24
64
|
|
|
@@ -29,15 +69,12 @@ export class LocalEmailProvider implements EmailProvider {
|
|
|
29
69
|
});
|
|
30
70
|
|
|
31
71
|
try {
|
|
32
|
-
// Ensure directory exists
|
|
33
|
-
await fs.mkdir(this.directory, { recursive: true });
|
|
34
|
-
|
|
35
72
|
// Create filename: emailcontact+date
|
|
36
73
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
37
74
|
for (const recipient of Array.isArray(to) ? to : [to]) {
|
|
38
75
|
const sanitizedEmail = recipient.replace(/[^a-zA-Z0-9@.-]/g, "_");
|
|
39
76
|
const filename = `${sanitizedEmail}+${timestamp}.html`;
|
|
40
|
-
const filepath =
|
|
77
|
+
const filepath = this.fs.join(this.directory, filename);
|
|
41
78
|
|
|
42
79
|
// Create HTML content
|
|
43
80
|
const htmlContent = this.createEmailHtml({
|
|
@@ -47,7 +84,7 @@ export class LocalEmailProvider implements EmailProvider {
|
|
|
47
84
|
});
|
|
48
85
|
|
|
49
86
|
// Write to file
|
|
50
|
-
await fs.writeFile(filepath, htmlContent
|
|
87
|
+
await this.fs.writeFile(filepath, htmlContent);
|
|
51
88
|
|
|
52
89
|
this.log.info("Email saved to local file", { filepath, to, subject });
|
|
53
90
|
}
|
|
@@ -1,69 +1,173 @@
|
|
|
1
|
-
import { $env, $hook, t } from "alepha";
|
|
1
|
+
import { $atom, $env, $hook, $use, type Static, t } from "alepha";
|
|
2
2
|
import { $logger } from "alepha/logger";
|
|
3
3
|
import type { Transporter } from "nodemailer";
|
|
4
4
|
import nodemailer from "nodemailer";
|
|
5
5
|
import { EmailError } from "../errors/EmailError.ts";
|
|
6
6
|
import type { EmailProvider, EmailSendOptions } from "./EmailProvider.ts";
|
|
7
7
|
|
|
8
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Environment variables for nodemailer configuration
|
|
12
|
+
*/
|
|
8
13
|
const envSchema = t.object({
|
|
9
|
-
EMAIL_HOST: t.
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
EMAIL_HOST: t.optional(
|
|
15
|
+
t.text({
|
|
16
|
+
description: "SMTP server host",
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
12
19
|
EMAIL_PORT: t.number({
|
|
13
20
|
default: 587,
|
|
14
21
|
description: "SMTP server port",
|
|
15
22
|
}),
|
|
16
|
-
EMAIL_USER: t.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
EMAIL_USER: t.optional(
|
|
24
|
+
t.text({
|
|
25
|
+
description: "SMTP authentication username",
|
|
26
|
+
}),
|
|
27
|
+
),
|
|
28
|
+
EMAIL_PASS: t.optional(
|
|
29
|
+
t.text({
|
|
30
|
+
description: "SMTP authentication password",
|
|
31
|
+
}),
|
|
32
|
+
),
|
|
33
|
+
EMAIL_FROM: t.optional(
|
|
34
|
+
t.text({
|
|
35
|
+
description: "Default from email address",
|
|
36
|
+
}),
|
|
37
|
+
),
|
|
25
38
|
EMAIL_SECURE: t.boolean({
|
|
26
39
|
default: false,
|
|
27
40
|
description: "Use secure connection (TLS)",
|
|
28
41
|
}),
|
|
29
42
|
});
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Custom transporter configuration.
|
|
34
|
-
* If provided, will override environment variables.
|
|
35
|
-
*/
|
|
36
|
-
transporter?: Transporter;
|
|
44
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Nodemailer connection pooling and rate limiting options
|
|
48
|
+
*/
|
|
49
|
+
export const nodemailerEmailOptions = $atom({
|
|
50
|
+
name: "alepha.email.nodemailer.options",
|
|
51
|
+
schema: t.object({
|
|
52
|
+
pool: t.optional(
|
|
53
|
+
t.boolean({
|
|
54
|
+
description: "Enable connection pooling",
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
maxConnections: t.optional(
|
|
58
|
+
t.number({
|
|
59
|
+
description: "Maximum number of connections in pool",
|
|
60
|
+
}),
|
|
61
|
+
),
|
|
62
|
+
maxMessages: t.optional(
|
|
63
|
+
t.number({
|
|
64
|
+
description: "Maximum messages per connection",
|
|
65
|
+
}),
|
|
66
|
+
),
|
|
67
|
+
rateDelta: t.optional(
|
|
68
|
+
t.number({
|
|
69
|
+
description: "Time in milliseconds between message sends",
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
rateLimit: t.optional(
|
|
73
|
+
t.number({
|
|
74
|
+
description: "Maximum number of messages per rateDelta",
|
|
75
|
+
}),
|
|
76
|
+
),
|
|
77
|
+
}),
|
|
78
|
+
default: {},
|
|
79
|
+
});
|
|
43
80
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
rateLimit?: number;
|
|
53
|
-
};
|
|
81
|
+
export type NodemailerEmailProviderOptions = Static<
|
|
82
|
+
typeof nodemailerEmailOptions.schema
|
|
83
|
+
>;
|
|
84
|
+
|
|
85
|
+
declare module "alepha" {
|
|
86
|
+
interface State {
|
|
87
|
+
[nodemailerEmailOptions.key]: NodemailerEmailProviderOptions;
|
|
88
|
+
}
|
|
54
89
|
}
|
|
55
90
|
|
|
91
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Email provider using Nodemailer for SMTP transport.
|
|
95
|
+
*
|
|
96
|
+
* Configuration is provided via environment variables:
|
|
97
|
+
* - EMAIL_HOST: SMTP server host
|
|
98
|
+
* - EMAIL_PORT: SMTP server port (default: 587)
|
|
99
|
+
* - EMAIL_USER: SMTP authentication username
|
|
100
|
+
* - EMAIL_PASS: SMTP authentication password
|
|
101
|
+
* - EMAIL_FROM: Default from email address
|
|
102
|
+
* - EMAIL_SECURE: Use secure connection (default: false)
|
|
103
|
+
*
|
|
104
|
+
* Advanced pooling/rate limiting options can be configured via atom:
|
|
105
|
+
* @see {@link nodemailerEmailOptions}
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* // Configure via environment variables
|
|
110
|
+
* // EMAIL_HOST=smtp.example.com
|
|
111
|
+
* // EMAIL_PORT=587
|
|
112
|
+
* // EMAIL_USER=user@example.com
|
|
113
|
+
* // EMAIL_PASS=secret
|
|
114
|
+
* // EMAIL_FROM=noreply@example.com
|
|
115
|
+
*
|
|
116
|
+
* // Optionally configure pooling via atom
|
|
117
|
+
* alepha.state.set(nodemailerEmailOptions.key, {
|
|
118
|
+
* pool: true,
|
|
119
|
+
* maxConnections: 5,
|
|
120
|
+
* rateLimit: 10,
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
56
124
|
export class NodemailerEmailProvider implements EmailProvider {
|
|
57
125
|
protected readonly env = $env(envSchema);
|
|
58
126
|
protected readonly log = $logger();
|
|
59
|
-
protected
|
|
60
|
-
protected
|
|
127
|
+
protected readonly options = $use(nodemailerEmailOptions);
|
|
128
|
+
protected transporter: Transporter | null = null;
|
|
61
129
|
|
|
62
|
-
|
|
130
|
+
protected get host(): string {
|
|
131
|
+
const host = this.env.EMAIL_HOST;
|
|
132
|
+
if (!host) {
|
|
133
|
+
throw new EmailError(
|
|
134
|
+
"Email host not configured. Set EMAIL_HOST env var.",
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return host;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
protected get port(): number {
|
|
141
|
+
return this.env.EMAIL_PORT;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
protected get secure(): boolean {
|
|
145
|
+
return this.env.EMAIL_SECURE;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
protected get user(): string | undefined {
|
|
149
|
+
return this.env.EMAIL_USER;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
protected get pass(): string | undefined {
|
|
153
|
+
return this.env.EMAIL_PASS;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected get fromAddress(): string {
|
|
157
|
+
const from = this.env.EMAIL_FROM;
|
|
158
|
+
if (!from) {
|
|
159
|
+
throw new EmailError(
|
|
160
|
+
"Email from address not configured. Set EMAIL_FROM env var.",
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return from;
|
|
164
|
+
}
|
|
63
165
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
166
|
+
protected getTransporter(): Transporter {
|
|
167
|
+
if (!this.transporter) {
|
|
168
|
+
this.transporter = this.createTransporter();
|
|
169
|
+
}
|
|
170
|
+
return this.transporter;
|
|
67
171
|
}
|
|
68
172
|
|
|
69
173
|
public async send(options: EmailSendOptions): Promise<void> {
|
|
@@ -71,7 +175,7 @@ export class NodemailerEmailProvider implements EmailProvider {
|
|
|
71
175
|
this.log.debug("Sending email via Nodemailer", { to, subject });
|
|
72
176
|
|
|
73
177
|
try {
|
|
74
|
-
const result = await this.
|
|
178
|
+
const result = await this.getTransporter().sendMail({
|
|
75
179
|
from: this.fromAddress,
|
|
76
180
|
to,
|
|
77
181
|
subject,
|
|
@@ -92,26 +196,30 @@ export class NodemailerEmailProvider implements EmailProvider {
|
|
|
92
196
|
}
|
|
93
197
|
|
|
94
198
|
protected createTransporter(): Transporter {
|
|
95
|
-
if (this.options.transporter) {
|
|
96
|
-
return this.options.transporter;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
199
|
const transporterConfig = {
|
|
100
|
-
host: this.
|
|
101
|
-
port: this.
|
|
102
|
-
secure: this.
|
|
103
|
-
auth:
|
|
104
|
-
user
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
200
|
+
host: this.host,
|
|
201
|
+
port: this.port,
|
|
202
|
+
secure: this.secure,
|
|
203
|
+
auth:
|
|
204
|
+
this.user && this.pass
|
|
205
|
+
? {
|
|
206
|
+
user: this.user,
|
|
207
|
+
pass: this.pass,
|
|
208
|
+
}
|
|
209
|
+
: undefined,
|
|
210
|
+
pool: this.options.pool,
|
|
211
|
+
maxConnections: this.options.maxConnections,
|
|
212
|
+
maxMessages: this.options.maxMessages,
|
|
213
|
+
rateDelta: this.options.rateDelta,
|
|
214
|
+
rateLimit: this.options.rateLimit,
|
|
108
215
|
};
|
|
109
216
|
|
|
110
217
|
this.log.debug("Creating Nodemailer transporter", {
|
|
111
218
|
host: transporterConfig.host,
|
|
112
219
|
port: transporterConfig.port,
|
|
113
220
|
secure: transporterConfig.secure,
|
|
114
|
-
user: transporterConfig.auth
|
|
221
|
+
user: transporterConfig.auth?.user,
|
|
222
|
+
pool: transporterConfig.pool,
|
|
115
223
|
});
|
|
116
224
|
|
|
117
225
|
return nodemailer.createTransport(transporterConfig);
|
|
@@ -122,7 +230,7 @@ export class NodemailerEmailProvider implements EmailProvider {
|
|
|
122
230
|
*/
|
|
123
231
|
public async verify(): Promise<boolean> {
|
|
124
232
|
try {
|
|
125
|
-
await this.
|
|
233
|
+
await this.getTransporter().verify();
|
|
126
234
|
this.log.info("Email server connection verified");
|
|
127
235
|
return true;
|
|
128
236
|
} catch (error) {
|
|
@@ -135,7 +243,10 @@ export class NodemailerEmailProvider implements EmailProvider {
|
|
|
135
243
|
* Close the transporter connection.
|
|
136
244
|
*/
|
|
137
245
|
public close(): void {
|
|
138
|
-
this.transporter
|
|
246
|
+
if (this.transporter) {
|
|
247
|
+
this.transporter.close();
|
|
248
|
+
this.transporter = null;
|
|
249
|
+
}
|
|
139
250
|
}
|
|
140
251
|
|
|
141
252
|
protected readonly onStart = $hook({
|
package/src/file/index.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { $module } from "alepha";
|
|
2
2
|
import { FileSystemProvider } from "./providers/FileSystemProvider.ts";
|
|
3
|
+
import { MemoryFileSystemProvider } from "./providers/MemoryFileSystemProvider.ts";
|
|
3
4
|
import { NodeFileSystemProvider } from "./providers/NodeFileSystemProvider.ts";
|
|
4
5
|
import { FileDetector } from "./services/FileDetector.ts";
|
|
5
6
|
|
|
6
7
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
7
8
|
|
|
9
|
+
export * from "./errors/FileError.ts";
|
|
8
10
|
export * from "./providers/FileSystemProvider.ts";
|
|
11
|
+
export * from "./providers/MemoryFileSystemProvider.ts";
|
|
9
12
|
export * from "./providers/NodeFileSystemProvider.ts";
|
|
10
13
|
export * from "./services/FileDetector.ts";
|
|
11
14
|
|
|
@@ -25,7 +28,12 @@ export * from "./services/FileDetector.ts";
|
|
|
25
28
|
export const AlephaFile = $module({
|
|
26
29
|
name: "alepha.file",
|
|
27
30
|
primitives: [],
|
|
28
|
-
services: [
|
|
31
|
+
services: [
|
|
32
|
+
FileDetector,
|
|
33
|
+
FileSystemProvider,
|
|
34
|
+
MemoryFileSystemProvider,
|
|
35
|
+
NodeFileSystemProvider,
|
|
36
|
+
],
|
|
29
37
|
register: (alepha) =>
|
|
30
38
|
alepha.with({
|
|
31
39
|
optional: true,
|