alepha 0.9.4 → 0.9.5

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/core.d.ts CHANGED
@@ -2,7 +2,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { TypeCheck } from "@sinclair/typebox/compiler";
3
3
  import * as TypeBoxValue from "@sinclair/typebox/value";
4
4
  import * as TypeBox from "@sinclair/typebox";
5
- import { ArrayOptions, FormatRegistry, IntegerOptions, NumberOptions, ObjectOptions, SchemaOptions, Static, Static as Static$1, StaticDecode, StaticEncode, StringOptions, TAny, TAny as TAny$1, TArray, TArray as TArray$1, TBoolean, TBoolean as TBoolean$1, TComposite, TInteger, TIntersect, TNull, TNumber, TNumber as TNumber$1, TObject, TObject as TObject$1, TOmit, TOptional, TOptionalWithFlag, TPartial, TPick, TProperties, TProperties as TProperties$1, TRecord, TRecord as TRecord$1, TSchema, TSchema as TSchema$1, TString, TString as TString$1, TUnion, TUnsafe, TypeGuard, UnsafeOptions } from "@sinclair/typebox";
5
+ import { ArrayOptions, FormatRegistry, IntegerOptions, NumberOptions, ObjectOptions, SchemaOptions, Static, Static as Static$1, StaticDecode, StaticEncode, StringOptions, TAny, TAny as TAny$1, TArray, TArray as TArray$1, TBoolean, TBoolean as TBoolean$1, TComposite, TInteger, TIntersect, TNull, TNull as TNull$1, TNumber, TNumber as TNumber$1, TObject, TObject as TObject$1, TOmit, TOptional, TOptionalWithFlag, TPartial, TPick, TProperties, TProperties as TProperties$1, TRecord, TRecord as TRecord$1, TSchema, TSchema as TSchema$1, TString, TString as TString$1, TUnion, TUnion as TUnion$1, TUnsafe, TypeGuard, UnsafeOptions } from "@sinclair/typebox";
6
6
  import { ValueError } from "@sinclair/typebox/errors";
7
7
  import { Readable } from "node:stream";
8
8
  import { ReadableStream as ReadableStream$1 } from "node:stream/web";
@@ -118,47 +118,42 @@ declare const $inject: <T extends object>(type: Service<T>, opts?: InjectOptions
118
118
  declare class InjectDescriptor extends Descriptor {}
119
119
  interface InjectOptions<T extends object = any> {
120
120
  /**
121
- * Ignore current existing instance.
122
- */
123
- skipCache?: boolean;
124
- /**
125
- * Don't store the instance in the registry.
121
+ * - 'transient' Always a new instance on every inject. Zero caching.
122
+ * - 'singleton' → One instance per Alepha runtime (per-thread). Never disposed until Alepha shuts down. (default)
123
+ * - 'scoped' → One instance per AsyncLocalStorage context.
124
+ * - A new scope is created when Alepha handles a request, a scheduled job, a queue worker task...
125
+ * - You can also start a manual scope via alepha.context.run(() => { ... }).
126
+ * - When the scope ends, the scoped registry is discarded.
127
+ *
128
+ * @default "singleton"
126
129
  */
127
- skipRegistration?: boolean;
130
+ lifetime?: "transient" | "singleton" | "scoped";
128
131
  /**
129
132
  * Constructor arguments to pass when creating a new instance.
130
133
  */
131
134
  args?: ConstructorParameters<InstantiableClass<T>>;
132
135
  /**
133
- * Parent service that requested the instance.
136
+ * Parent that requested the instance.
137
+ *
134
138
  * @internal
135
139
  */
136
140
  parent?: Service | null;
137
141
  }
138
142
  //#endregion
139
- //#region src/constants/OPTIONS.d.ts
140
- /**
141
- * Used for descriptors options.
142
- *
143
- * @internal
144
- */
145
- declare const OPTIONS: unique symbol;
146
- //#endregion
147
143
  //#region src/descriptors/$module.d.ts
148
144
  /**
149
- * Wrap services and descriptors into a module.
150
- *
151
- * Module is just a class.
152
- * You must attach a `name` to it.
145
+ * Wrap Services and Descriptors into a Module.
153
146
  *
154
- * It's recommended to use `project.module.submodule` format.
147
+ * - A module is just a Service extended {@link Module}.
148
+ * - You must attach a `name` to it.
149
+ * - Name must follow the pattern: `project.module.submodule`.
155
150
  *
156
151
  * @example
157
152
  * ```ts
158
153
  * import { $module } from "alepha";
159
154
  * import { MyService } from "./MyService.ts";
160
155
  *
161
- * // export MyService so it can be used everywhere
156
+ * // export MyService, so it can be used everywhere
162
157
  * export * from "./MyService.ts";
163
158
  *
164
159
  * export default $module({
@@ -168,9 +163,30 @@ declare const OPTIONS: unique symbol;
168
163
  * });
169
164
  * ```
170
165
  *
171
- * - Module is used for logging and other purposes.
172
- * - It's useful for large applications or libraries to group services and descriptors together.
173
- * - It's probably overkill for small applications.
166
+ * ## Why Modules?
167
+ *
168
+ * ### Logging
169
+ *
170
+ * By default, AlephaLogger will log the module name in the logs.
171
+ * This helps to identify where the logs are coming from.
172
+ *
173
+ * You can also set different log levels for different modules.
174
+ * It means you can set 'some.very.specific.module' to 'debug' and keep the rest of the application to 'info'.
175
+ *
176
+ * ### Modulith
177
+ *
178
+ * Force to structure your application in modules, even if it's a single deployable unit.
179
+ * It helps to keep a clean architecture and avoid monolithic applications.
180
+ *
181
+ * You can also use `MODULE_INCLUDE` and `MODULE_EXCLUDE` environment variables to load only specific modules.
182
+ *
183
+ * A strict mode is planned to enforce module boundaries. Throwing errors when a service from another module is injected.
184
+ *
185
+ * ### When not to use Modules?
186
+ *
187
+ * Small applications does not need modules. It's better to keep it simple.
188
+ * Modules are more useful when the application grows and needs to be structured.
189
+ * If we speak with `$actions`, a module should be used when you have more than 30 actions in a single module.
174
190
  */
175
191
  declare const $module: (options: ModuleDescriptorOptions) => Service<Module>;
176
192
  interface ModuleDescriptorOptions {
@@ -195,16 +211,25 @@ interface ModuleDescriptorOptions {
195
211
  */
196
212
  register?: (alepha: Alepha) => void;
197
213
  }
198
- interface Module {
199
- [KIND]: "MODULE";
200
- [OPTIONS]: ModuleDescriptorOptions;
201
- register: (alepha: Alepha) => void;
214
+ /**
215
+ * Base class for all modules.
216
+ */
217
+ declare abstract class Module {
218
+ abstract readonly options: ModuleDescriptorOptions;
219
+ abstract register(alepha: Alepha): void;
220
+ static NAME_REGEX: RegExp;
221
+ /**
222
+ * Check if a Service is a Module.
223
+ */
224
+ static is(ctor: Service): boolean;
225
+ /**
226
+ * Get the Module of a Service.
227
+ */
228
+ static of(ctor: Service): Service<Module> | undefined;
202
229
  }
203
- type ServiceWithModule<T extends object = any> = T & {
230
+ type WithModule<T extends object = any> = T & {
204
231
  [MODULE]?: Service;
205
232
  };
206
- declare const isModule: (value: unknown) => value is Module;
207
- declare const toModuleName: (name: string) => string;
208
233
  //#endregion
209
234
  //#region src/interfaces/Async.d.ts
210
235
  /**
@@ -364,6 +389,8 @@ declare class AlsProvider {
364
389
  * }
365
390
  * }
366
391
  * ```
392
+ *
393
+ * @module alepha
367
394
  */
368
395
  declare class Alepha {
369
396
  /**
@@ -556,15 +583,27 @@ declare class Alepha {
556
583
  /**
557
584
  * Check if the entry is registered in the pending instantiation stack.
558
585
  *
559
- * Default: true
586
+ * @default true
560
587
  */
561
588
  inStack?: boolean;
562
589
  /**
563
590
  * Check if the entry is registered in the container registry.
564
591
  *
565
- * Default: true
592
+ * @default true
566
593
  */
567
594
  inRegistry?: boolean;
595
+ /**
596
+ * Check if the entry is registered in the substitutions.
597
+ *
598
+ * @default true
599
+ */
600
+ inSubstitutions?: boolean;
601
+ /**
602
+ * Where to look for registered services.
603
+ *
604
+ * @default this.registry
605
+ */
606
+ registry?: Map<Service, ServiceDefinition>;
568
607
  }): boolean;
569
608
  /**
570
609
  * Registers the specified service in the container.
@@ -596,14 +635,9 @@ declare class Alepha {
596
635
  default: ServiceEntry<T>;
597
636
  }): this;
598
637
  /**
599
- * Get the instance of the specified service and apply some changes, depending on the options.
600
- * - If the service is already registered, it will return the existing instance. (except if `skipCache` is true)
601
- * - If the service is not registered, it will create a new instance and register it. (except if `skipRegistration` is true)
602
- * - New instance can be created with custom constructor arguments. (`args` option)
603
- *
604
- * > This method is used by $inject() under the hood.
638
+ * Get an instance of the specified service from the container.
605
639
  *
606
- * @return The instance of the specified class or type.
640
+ * @see {@link InjectOptions} for the available options.
607
641
  */
608
642
  inject<T extends object>(service: Service<T>, opts?: InjectOptions<T>): T;
609
643
  /**
@@ -660,6 +694,11 @@ declare class Alepha {
660
694
  * It uses the TypeBox library to validate the value against the schema.
661
695
  */
662
696
  parse<T extends TSchema$1>(schema: T, value?: any, opts?: {
697
+ /**
698
+ * Convert `null` to `undefined`
699
+ * @default true
700
+ */
701
+ convertNullToUndefined?: boolean;
663
702
  /**
664
703
  * Clone the value before parsing.
665
704
  * @default true
@@ -729,10 +768,6 @@ interface ServiceDefinition<T extends object = any> {
729
768
  * List of classes which use this class.
730
769
  */
731
770
  parents: Array<Service | null>;
732
- /**
733
- * If the service is provided by a module, the module definition.
734
- */
735
- module?: Service;
736
771
  }
737
772
  interface Env {
738
773
  [key: string]: string | boolean | number | undefined;
@@ -828,6 +863,14 @@ interface RunOptions {
828
863
  cluster?: boolean;
829
864
  }
830
865
  //#endregion
866
+ //#region src/constants/OPTIONS.d.ts
867
+ /**
868
+ * Used for descriptors options.
869
+ *
870
+ * @internal
871
+ */
872
+ declare const OPTIONS: unique symbol;
873
+ //#endregion
831
874
  //#region src/constants/PRIMITIVE.d.ts
832
875
  /**
833
876
  * Symbol to mark a value as a primitive.
@@ -993,25 +1036,31 @@ declare class AlephaError extends Error {
993
1036
  }
994
1037
  //#endregion
995
1038
  //#region src/errors/AppNotStartedError.d.ts
996
- declare class AppNotStartedError extends Error {
1039
+ declare class AppNotStartedError extends AlephaError {
997
1040
  readonly name = "AppNotStartedError";
998
1041
  constructor();
999
1042
  }
1000
1043
  //#endregion
1001
1044
  //#region src/errors/CircularDependencyError.d.ts
1002
- declare class CircularDependencyError extends Error {
1045
+ declare class CircularDependencyError extends AlephaError {
1003
1046
  readonly name = "CircularDependencyError";
1004
1047
  constructor(provider: string, parents?: string[]);
1005
1048
  }
1006
1049
  //#endregion
1007
1050
  //#region src/errors/ContainerLockedError.d.ts
1008
- declare class ContainerLockedError extends Error {
1051
+ declare class ContainerLockedError extends AlephaError {
1009
1052
  readonly name = "ContainerLockedError";
1010
1053
  constructor(message?: string);
1011
1054
  }
1012
1055
  //#endregion
1056
+ //#region src/errors/TooLateSubstitutionError.d.ts
1057
+ declare class TooLateSubstitutionError extends AlephaError {
1058
+ readonly name = "TooLateSubstitutionError";
1059
+ constructor(original: string, substitution: string);
1060
+ }
1061
+ //#endregion
1013
1062
  //#region src/errors/TypeBoxError.d.ts
1014
- declare class TypeBoxError extends Error {
1063
+ declare class TypeBoxError extends AlephaError {
1015
1064
  readonly name = "TypeBoxError";
1016
1065
  readonly value: ValueError;
1017
1066
  constructor(value: ValueError);
@@ -1152,8 +1201,8 @@ declare class TypeProvider {
1152
1201
  * @param schema The schema to make nullable.
1153
1202
  * @param options The options for the schema.
1154
1203
  */
1155
- nullable<T extends TSchema$1>(schema: T, options?: ObjectOptions): TUnion<[TNull, T]>;
1156
- nullify: <T extends TSchema$1>(schema: T, options?: ObjectOptions) => TObject$1<TypeBox.Evaluate<TypeBox.TMappedFunctionReturnType<TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>>, TUnion<[TNull, TypeBox.TMappedResult<TypeBox.Evaluate<TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>> extends infer T_1 ? T_1 extends TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>> ? T_1 extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? /*elided*/any : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : {} : never : never>>]>, {}>>>;
1204
+ nullable<T extends TSchema$1>(schema: T, options?: ObjectOptions): TUnion$1<[TNull$1, T]>;
1205
+ nullify: <T extends TSchema$1>(schema: T, options?: ObjectOptions) => TObject$1<TypeBox.Evaluate<TypeBox.TMappedFunctionReturnType<TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>>, TUnion$1<[TNull$1, TypeBox.TMappedResult<TypeBox.Evaluate<TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>> extends infer T_1 ? T_1 extends TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>> ? T_1 extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? /*elided*/any : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : {} : never : never>>]>, {}>>>;
1157
1206
  /**
1158
1207
  * Map a schema to another schema.
1159
1208
  *
@@ -1272,7 +1321,6 @@ declare const isTypeFile: (value: TSchema$1) => value is TFile;
1272
1321
  declare const isFileLike: (value: any) => value is FileLike;
1273
1322
  type StreamLike = ReadableStream | ReadableStream$1 | Readable | NodeJS.ReadableStream;
1274
1323
  type TStream = TUnsafe<StreamLike>;
1275
- declare const isTypeStream: (value: TSchema$1) => value is TStream;
1276
1324
  type TextLength = "short" | "long" | "rich";
1277
1325
  interface AlephaStringOptions extends StringOptions {
1278
1326
  size?: TextLength;
@@ -1297,5 +1345,5 @@ declare global {
1297
1345
  */
1298
1346
  declare const run: (entry: Alepha | Service | Array<Service>, opts?: RunOptions) => void;
1299
1347
  //#endregion
1300
- export { $cursor, $env, $hook, $inject, $module, AbstractClass, Alepha, AlephaError, AlephaStringOptions, AlsProvider, AppNotStartedError, Async, AsyncFn, AsyncLocalStorageData, CircularDependencyError, ContainerLockedError, CursorDescriptor, Descriptor, DescriptorArgs, DescriptorConfig, DescriptorFactory, DescriptorFactoryLike, Env, FileLike, Hook, HookDescriptor, HookOptions, Hooks, InjectDescriptor, InjectOptions, InstantiableClass, KIND, LogLevel, LoggerInterface, MaybePromise, Module, ModuleDescriptorOptions, OPTIONS, PRIMITIVE, Service, ServiceEntry, ServiceSubstitution, ServiceWithModule, State, type Static, type StaticDecode, type StaticEncode, StreamLike, type TAny, type TArray, type TBoolean, TFile, type TNumber, type TObject, type TOptional, type TProperties, type TRecord, type TSchema, TStream, type TString, TextLength, TypeBox, TypeBoxError, TypeBoxValue, TypeGuard, TypeProvider, __alephaRef, createDescriptor, isEmail, isFileLike, isISODate, isISODateTime, isModule, isTypeFile, isTypeStream, isUUID, run, t, toModuleName };
1348
+ export { $cursor, $env, $hook, $inject, $module, AbstractClass, Alepha, AlephaError, AlephaStringOptions, AlsProvider, AppNotStartedError, Async, AsyncFn, AsyncLocalStorageData, CircularDependencyError, ContainerLockedError, CursorDescriptor, Descriptor, DescriptorArgs, DescriptorConfig, DescriptorFactory, DescriptorFactoryLike, Env, FileLike, Hook, HookDescriptor, HookOptions, Hooks, InjectDescriptor, InjectOptions, InstantiableClass, KIND, LogLevel, LoggerInterface, MaybePromise, Module, ModuleDescriptorOptions, OPTIONS, PRIMITIVE, Service, ServiceEntry, ServiceSubstitution, State, type Static, type StaticDecode, type StaticEncode, StreamLike, type TAny, type TArray, type TBoolean, TFile, type TNull, type TNumber, type TObject, type TOptional, type TProperties, type TRecord, type TSchema, TStream, type TString, type TUnion, TextLength, TooLateSubstitutionError, TypeBox, TypeBoxError, TypeBoxValue, TypeGuard, TypeProvider, WithModule, __alephaRef, createDescriptor, isEmail, isFileLike, isISODate, isISODateTime, isTypeFile, isUUID, run, t };
1301
1349
  //# sourceMappingURL=index.d.ts.map
package/email.cjs ADDED
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+ var m = require('@alepha/email');
3
+ Object.keys(m).forEach(function (k) {
4
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
5
+ enumerable: true,
6
+ get: function () { return m[k]; }
7
+ });
8
+ });
package/email.d.ts ADDED
@@ -0,0 +1,246 @@
1
+ import * as _alepha_core1 from "alepha";
2
+ import { Descriptor, KIND, Service, Static, TSchema } from "alepha";
3
+ import * as _alepha_logger0 from "alepha/logger";
4
+ import { Transporter } from "nodemailer";
5
+
6
+ //#region src/providers/EmailProvider.d.ts
7
+ /**
8
+ * Email provider interface.
9
+ *
10
+ * All methods are asynchronous and return promises.
11
+ */
12
+ declare abstract class EmailProvider {
13
+ /**
14
+ * Send an email.
15
+ *
16
+ * @param to The recipient email address.
17
+ * @param subject The email subject.
18
+ * @param body The email body (HTML content).
19
+ *
20
+ * @return Promise that resolves when the email is sent.
21
+ */
22
+ abstract send(to: string, subject: string, body: string): Promise<void>;
23
+ }
24
+ //#endregion
25
+ //#region src/providers/MemoryEmailProvider.d.ts
26
+ interface EmailRecord {
27
+ to: string;
28
+ subject: string;
29
+ body: string;
30
+ sentAt: Date;
31
+ }
32
+ declare class MemoryEmailProvider implements EmailProvider {
33
+ protected readonly log: _alepha_logger0.Logger;
34
+ protected emails: EmailRecord[];
35
+ send(to: string, subject: string, body: string): Promise<void>;
36
+ /**
37
+ * Get all emails sent through this provider (for testing purposes).
38
+ */
39
+ getEmails(): EmailRecord[];
40
+ /**
41
+ * Clear all stored emails (for testing purposes).
42
+ */
43
+ clearEmails(): void;
44
+ /**
45
+ * Get the last email sent (for testing purposes).
46
+ */
47
+ getLastEmail(): EmailRecord | undefined;
48
+ }
49
+ //#endregion
50
+ //#region src/services/TemplateService.d.ts
51
+ /**
52
+ * Minimal template service with Handlebars-like syntax for email templating.
53
+ * Supports simple variable substitution with {{variableName}} syntax.
54
+ */
55
+ declare class TemplateService {
56
+ /**
57
+ * Compile a template string with the provided values.
58
+ *
59
+ * @param template Template string with {{variableName}} placeholders
60
+ * @param values Object containing values to substitute
61
+ * @returns Compiled template string with values substituted
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * const service = new TemplateService();
66
+ * const result = service.compile("Hello {{name}}!", { name: "John" });
67
+ * // Result: "Hello John!"
68
+ * ```
69
+ */
70
+ compile(template: string, values: Record<string, unknown>): string;
71
+ /**
72
+ * Validate that all required template variables are provided.
73
+ *
74
+ * @param template Template string
75
+ * @param values Values object
76
+ * @returns Array of missing variable names
77
+ */
78
+ validateTemplate(template: string, values: Record<string, unknown>): string[];
79
+ /**
80
+ * Extract all variable names from a template.
81
+ *
82
+ * @param template Template string
83
+ * @returns Array of variable names found in the template
84
+ */
85
+ extractVariables(template: string): string[];
86
+ }
87
+ //#endregion
88
+ //#region src/descriptors/$email.d.ts
89
+ /**
90
+ * Create an email descriptor for sending templated emails.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import { $email } from "alepha/email";
95
+ * import { t } from "alepha";
96
+ *
97
+ * class App {
98
+ * welcome = $email({
99
+ * subject: "Welcome {{name}}!",
100
+ * body: "<h1>Welcome {{name}}!</h1><p>Your role is {{role}}.</p>",
101
+ * schema: t.object({
102
+ * name: t.string(),
103
+ * role: t.string()
104
+ * })
105
+ * });
106
+ *
107
+ * async sendWelcome(userEmail: string, name: string, role: string) {
108
+ * await this.welcome.send(userEmail, { name, role });
109
+ * }
110
+ * }
111
+ * ```
112
+ */
113
+ declare const $email: {
114
+ <T extends TSchema>(options: EmailDescriptorOptions<T>): EmailDescriptor<T>;
115
+ [KIND]: typeof EmailDescriptor;
116
+ };
117
+ interface EmailDescriptorOptions<T extends TSchema> {
118
+ /**
119
+ * Email subject template. Supports {{variableName}} syntax.
120
+ */
121
+ subject: string;
122
+ /**
123
+ * Email body template (HTML content). Supports {{variableName}} syntax.
124
+ */
125
+ body: string;
126
+ /**
127
+ * Schema defining the structure of template variables.
128
+ */
129
+ schema: T;
130
+ /**
131
+ * Optional name of the email template.
132
+ * @default Descriptor key
133
+ */
134
+ name?: string;
135
+ /**
136
+ * Optional description of the email template.
137
+ */
138
+ description?: string;
139
+ /**
140
+ * Email provider to use. If not provided, the default provider will be used.
141
+ */
142
+ provider?: Service<EmailProvider> | "memory";
143
+ }
144
+ declare class EmailDescriptor<T extends TSchema> extends Descriptor<EmailDescriptorOptions<T>> {
145
+ protected readonly log: _alepha_logger0.Logger;
146
+ protected readonly templateService: TemplateService;
147
+ readonly provider: EmailProvider | MemoryEmailProvider;
148
+ get name(): string;
149
+ /**
150
+ * Send an email using the template with the provided values.
151
+ *
152
+ * @param to Recipient email address
153
+ * @param values Template variable values
154
+ */
155
+ send(to: string, values: Static<T>): Promise<void>;
156
+ protected $provider(): EmailProvider | MemoryEmailProvider;
157
+ }
158
+ //#endregion
159
+ //#region src/errors/EmailError.d.ts
160
+ declare class EmailError extends Error {
161
+ constructor(message: string, cause?: Error);
162
+ }
163
+ //#endregion
164
+ //#region src/providers/LocalEmailProvider.d.ts
165
+ interface LocalEmailProviderOptions {
166
+ /**
167
+ * Directory to save email files.
168
+ * @default "email" (relative to project root)
169
+ */
170
+ directory?: string;
171
+ }
172
+ declare class LocalEmailProvider implements EmailProvider {
173
+ protected readonly log: _alepha_logger0.Logger;
174
+ protected readonly directory: string;
175
+ constructor(options?: LocalEmailProviderOptions);
176
+ send(to: string, subject: string, body: string): Promise<void>;
177
+ protected createEmailHtml(to: string, subject: string, body: string): string;
178
+ protected escapeHtml(text: string): string;
179
+ }
180
+ //#endregion
181
+ //#region src/providers/NodemailerEmailProvider.d.ts
182
+ interface NodemailerEmailProviderOptions {
183
+ /**
184
+ * Custom transporter configuration.
185
+ * If provided, will override environment variables.
186
+ */
187
+ transporter?: Transporter;
188
+ /**
189
+ * Custom from email address.
190
+ * If not provided, will use EMAIL_FROM from environment.
191
+ */
192
+ from?: string;
193
+ /**
194
+ * Additional nodemailer options.
195
+ */
196
+ options?: {
197
+ pool?: boolean;
198
+ maxConnections?: number;
199
+ maxMessages?: number;
200
+ rateDelta?: number;
201
+ rateLimit?: number;
202
+ };
203
+ }
204
+ declare class NodemailerEmailProvider implements EmailProvider {
205
+ protected readonly env: {
206
+ EMAIL_HOST: string;
207
+ EMAIL_PORT: number;
208
+ EMAIL_USER: string;
209
+ EMAIL_PASS: string;
210
+ EMAIL_FROM: string;
211
+ EMAIL_SECURE: boolean;
212
+ };
213
+ protected readonly log: _alepha_logger0.Logger;
214
+ protected transporter: Transporter;
215
+ protected fromAddress: string;
216
+ readonly options: NodemailerEmailProviderOptions;
217
+ constructor();
218
+ send(to: string, subject: string, body: string): Promise<void>;
219
+ protected createTransporter(): Transporter;
220
+ /**
221
+ * Verify the connection to the email server.
222
+ */
223
+ verify(): Promise<boolean>;
224
+ /**
225
+ * Close the transporter connection.
226
+ */
227
+ close(): void;
228
+ protected readonly onStart: _alepha_core1.HookDescriptor<"start">;
229
+ protected readonly onStop: _alepha_core1.HookDescriptor<"stop">;
230
+ }
231
+ //#endregion
232
+ //#region src/index.d.ts
233
+ /**
234
+ * Provides email sending capabilities for Alepha applications with multiple provider backends.
235
+ *
236
+ * The email module enables declarative email sending through the `$email` descriptor, allowing you to send
237
+ * emails through different providers: memory (for testing), local file system, or SMTP via Nodemailer.
238
+ * It supports HTML email content and automatic provider selection based on environment configuration.
239
+ *
240
+ * @see {@link EmailProvider}
241
+ * @module alepha.email
242
+ */
243
+ declare const AlephaEmail: _alepha_core1.Service<_alepha_core1.Module>;
244
+ //#endregion
245
+ export { $email, AlephaEmail, EmailDescriptor, EmailDescriptorOptions, EmailError, EmailProvider, EmailRecord, LocalEmailProvider, LocalEmailProviderOptions, MemoryEmailProvider, NodemailerEmailProvider, NodemailerEmailProviderOptions, TemplateService };
246
+ //# sourceMappingURL=index.d.ts.map
package/email.js ADDED
@@ -0,0 +1 @@
1
+ export * from '@alepha/email'