@pattern-js/mod-email-smtp 0.3.0-main.4a77893
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/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/driver.d.ts +24 -0
- package/dist/driver.d.ts.map +1 -0
- package/dist/driver.js +64 -0
- package/dist/driver.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/docs/index.md +39 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Makkr Studio
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @pattern-js/mod-email-smtp
|
|
2
|
+
|
|
3
|
+
The SMTP driver for
|
|
4
|
+
[`@pattern-js/mod-email`](https://www.npmjs.com/package/@pattern-js/mod-email)
|
|
5
|
+
on [Pattern](../../README.md), built on nodemailer — any relay, your own
|
|
6
|
+
server, or a local catcher (Mailpit) in dev. Host/port/user are account
|
|
7
|
+
options in admin → System → Email; the password comes from the vault or an
|
|
8
|
+
env var.
|
|
9
|
+
|
|
10
|
+
**Links:** [pattern-js.dev](https://pattern-js.dev) · [npm](https://www.npmjs.com/package/@pattern-js/mod-email-smtp)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @pattern-js/mod-email @pattern-js/mod-email-smtp
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## When to use / when not
|
|
17
|
+
|
|
18
|
+
Reach for it when you already have SMTP credentials (SES, Postmark, Mailgun,
|
|
19
|
+
a corporate relay) or want a local catcher in dev. Prefer
|
|
20
|
+
[`mod-email-resend`](https://www.npmjs.com/package/@pattern-js/mod-email-resend)
|
|
21
|
+
for the least-setup path to production email (one API key, no relay).
|
|
22
|
+
|
|
23
|
+
## Config
|
|
24
|
+
|
|
25
|
+
Nothing to configure in code — list it and manage accounts in the admin:
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
{ "mods": ["@pattern-js/mod-email", "@pattern-js/mod-email-smtp"] }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Account fields: `host` (required), `port` (587), `secure`
|
|
32
|
+
(`"true"` = implicit TLS; default STARTTLS), `user`, and the `pass` secret
|
|
33
|
+
(vault or env, e.g. `SMTP_PASSWORD`). For tests, the mod factory accepts a
|
|
34
|
+
`transportFactory` seam: `smtpEmailMod({ transportFactory })`.
|
|
35
|
+
|
|
36
|
+
Full documentation: the **Email · SMTP** chapter at `/docs`, or
|
|
37
|
+
[the source](docs/index.md).
|
package/dist/driver.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pattern-js/mod-email-smtp — the driver.
|
|
3
|
+
*
|
|
4
|
+
* nodemailer does the protocol work (STARTTLS vs implicit TLS, AUTH, MIME) —
|
|
5
|
+
* hand-rolling SMTP is a correctness-and-security tarpit this mod deliberately
|
|
6
|
+
* avoids. Transports pool connections, so they're cached per CONFIG: the key
|
|
7
|
+
* is a sha256 of {host,port,secure,user,pass}, which is functionally
|
|
8
|
+
* per-account and self-invalidates when credentials rotate (only the digest is
|
|
9
|
+
* ever retained). The `transportFactory` option is the test seam.
|
|
10
|
+
*/
|
|
11
|
+
import { type Transporter } from "nodemailer";
|
|
12
|
+
import type { EmailDriverSpec } from "@pattern-js/mod-email";
|
|
13
|
+
export interface SmtpTransportConfig {
|
|
14
|
+
host: string;
|
|
15
|
+
port: number;
|
|
16
|
+
secure: boolean;
|
|
17
|
+
auth?: {
|
|
18
|
+
user: string;
|
|
19
|
+
pass?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export type TransportFactory = (config: SmtpTransportConfig) => Transporter;
|
|
23
|
+
export declare function smtpDriver(factory?: TransportFactory): EmailDriverSpec;
|
|
24
|
+
//# sourceMappingURL=driver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,mBAAmB,KAAK,WAAW,CAAC;AAE5E,wBAAgB,UAAU,CAAC,OAAO,GAAE,gBAAiE,GAAG,eAAe,CAkDtH"}
|
package/dist/driver.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pattern-js/mod-email-smtp — the driver.
|
|
3
|
+
*
|
|
4
|
+
* nodemailer does the protocol work (STARTTLS vs implicit TLS, AUTH, MIME) —
|
|
5
|
+
* hand-rolling SMTP is a correctness-and-security tarpit this mod deliberately
|
|
6
|
+
* avoids. Transports pool connections, so they're cached per CONFIG: the key
|
|
7
|
+
* is a sha256 of {host,port,secure,user,pass}, which is functionally
|
|
8
|
+
* per-account and self-invalidates when credentials rotate (only the digest is
|
|
9
|
+
* ever retained). The `transportFactory` option is the test seam.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { Buffer } from "node:buffer";
|
|
13
|
+
import nodemailer from "nodemailer";
|
|
14
|
+
export function smtpDriver(factory = (config) => nodemailer.createTransport(config)) {
|
|
15
|
+
const cache = new Map();
|
|
16
|
+
function transportFor(creds, options) {
|
|
17
|
+
if (!options.host)
|
|
18
|
+
throw new Error('smtp: the account is missing its "host" option.');
|
|
19
|
+
const config = {
|
|
20
|
+
host: options.host,
|
|
21
|
+
port: Number(options.port || 587),
|
|
22
|
+
secure: options.secure === "true",
|
|
23
|
+
auth: options.user ? { user: options.user, pass: creds.pass } : undefined,
|
|
24
|
+
};
|
|
25
|
+
const key = createHash("sha256").update(JSON.stringify(config)).digest("hex");
|
|
26
|
+
let transport = cache.get(key);
|
|
27
|
+
if (!transport) {
|
|
28
|
+
transport = factory(config);
|
|
29
|
+
cache.set(key, transport);
|
|
30
|
+
}
|
|
31
|
+
return transport;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
id: "smtp",
|
|
35
|
+
label: "SMTP",
|
|
36
|
+
// Optional on purpose: unauthenticated relays (and local catchers) exist.
|
|
37
|
+
secrets: [{ field: "pass", label: "Password", required: false }],
|
|
38
|
+
options: [
|
|
39
|
+
{ field: "host", label: "Host", required: true, placeholder: "smtp.example.com" },
|
|
40
|
+
{ field: "port", label: "Port", required: false, placeholder: "587" },
|
|
41
|
+
{ field: "secure", label: "Implicit TLS (true/false)", required: false, placeholder: "false = STARTTLS on 587" },
|
|
42
|
+
{ field: "user", label: "Username", required: false, placeholder: "" },
|
|
43
|
+
],
|
|
44
|
+
async send(message, creds, options) {
|
|
45
|
+
const info = await transportFor(creds, options).sendMail({
|
|
46
|
+
from: message.from,
|
|
47
|
+
to: message.to,
|
|
48
|
+
cc: message.cc,
|
|
49
|
+
bcc: message.bcc,
|
|
50
|
+
replyTo: message.replyTo,
|
|
51
|
+
subject: message.subject,
|
|
52
|
+
html: message.html,
|
|
53
|
+
text: message.text,
|
|
54
|
+
attachments: message.attachments?.map((a) => ({
|
|
55
|
+
filename: a.filename,
|
|
56
|
+
content: Buffer.from(a.content),
|
|
57
|
+
contentType: a.mime,
|
|
58
|
+
})),
|
|
59
|
+
});
|
|
60
|
+
return { messageId: info?.messageId };
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pattern-js/mod-email-smtp — SMTP driver for mod-email.
|
|
3
|
+
*
|
|
4
|
+
* All the sending logic lives in driver.ts; this mod just registers it on
|
|
5
|
+
* mod-email's service in `ready` (after every setup ran — two-phase install,
|
|
6
|
+
* so listing order in pattern.config.json doesn't matter). Host/port/user are
|
|
7
|
+
* account options in admin → System → Email; the password comes from the
|
|
8
|
+
* vault or an env var.
|
|
9
|
+
*/
|
|
10
|
+
import { type PatternMod } from "@pattern-js/core";
|
|
11
|
+
import { type TransportFactory } from "./driver.js";
|
|
12
|
+
export { smtpDriver, type SmtpTransportConfig, type TransportFactory } from "./driver.js";
|
|
13
|
+
export interface SmtpEmailModOptions {
|
|
14
|
+
/** Override how transports are built (the test seam; default nodemailer.createTransport). */
|
|
15
|
+
transportFactory?: TransportFactory;
|
|
16
|
+
}
|
|
17
|
+
export declare function smtpEmailMod(options?: SmtpEmailModOptions): PatternMod;
|
|
18
|
+
/** Ready-to-use with defaults (for `loadMods`/`engine.use`). */
|
|
19
|
+
declare const _default: PatternMod;
|
|
20
|
+
export default _default;
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE3E,OAAO,EAAwC,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE1F,OAAO,EAAE,UAAU,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE1F,MAAM,WAAW,mBAAmB;IAClC,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAYD,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,UAAU,CAkB1E;AAED,gEAAgE;;AAChE,wBAA8B"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pattern-js/mod-email-smtp — SMTP driver for mod-email.
|
|
3
|
+
*
|
|
4
|
+
* All the sending logic lives in driver.ts; this mod just registers it on
|
|
5
|
+
* mod-email's service in `ready` (after every setup ran — two-phase install,
|
|
6
|
+
* so listing order in pattern.config.json doesn't matter). Host/port/user are
|
|
7
|
+
* account options in admin → System → Email; the password comes from the
|
|
8
|
+
* vault or an env var.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { localFs, provideFilesystem } from "@pattern-js/runtime-node";
|
|
13
|
+
import { defineMod } from "@pattern-js/core";
|
|
14
|
+
import { EMAIL_SERVICE } from "@pattern-js/mod-email";
|
|
15
|
+
import { smtpDriver } from "./driver.js";
|
|
16
|
+
export { smtpDriver } from "./driver.js";
|
|
17
|
+
/** The packaged docs/ chapter (the `docs` contribution points at "email-smtp-docs"). */
|
|
18
|
+
function packagedDocs(engine) {
|
|
19
|
+
try {
|
|
20
|
+
const dir = fileURLToPath(new URL("../docs", import.meta.url));
|
|
21
|
+
if (existsSync(dir))
|
|
22
|
+
provideFilesystem(engine, "email-smtp-docs", localFs(dir));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* packaged without docs — the contribution is simply skipped */
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function smtpEmailMod(options = {}) {
|
|
29
|
+
return defineMod({
|
|
30
|
+
name: "@pattern-js/mod-email-smtp",
|
|
31
|
+
docs: { filesystem: "email-smtp-docs", title: "Email · SMTP", order: 45 },
|
|
32
|
+
// `ready`, not `setup`: mod-email provides its service in setup, and every
|
|
33
|
+
// setup runs before any ready — order in the config is free.
|
|
34
|
+
ready: (engine) => {
|
|
35
|
+
packagedDocs(engine);
|
|
36
|
+
const svc = engine.service(EMAIL_SERVICE);
|
|
37
|
+
if (!svc) {
|
|
38
|
+
console.error("[pattern] @pattern-js/mod-email-smtp: email service not found — add @pattern-js/mod-email to your mods.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
svc.registerDriver(smtpDriver(options.transportFactory));
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Ready-to-use with defaults (for `loadMods`/`engine.use`). */
|
|
46
|
+
export default smtpEmailMod();
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,SAAS,EAAgC,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAqB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAmD,MAAM,aAAa,CAAC;AAE1F,OAAO,EAAE,UAAU,EAAmD,MAAM,aAAa,CAAC;AAO1F,wFAAwF;AACxF,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAClF,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAA+B,EAAE;IAC5D,OAAO,SAAS,CAAC;QACf,IAAI,EAAE,4BAA4B;QAClC,IAAI,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE;QACzE,2EAA2E;QAC3E,6DAA6D;QAC7D,KAAK,EAAE,CAAC,MAAc,EAAE,EAAE;YACxB,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAe,aAAa,CAAC,CAAC;YACxD,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,CAAC,KAAK,CACX,yGAAyG,CAC1G,CAAC;gBACF,OAAO;YACT,CAAC;YACD,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC3D,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,gEAAgE;AAChE,eAAe,YAAY,EAAE,CAAC"}
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Email · SMTP
|
|
2
|
+
|
|
3
|
+
`@pattern-js/mod-email-smtp` is the SMTP driver for `@pattern-js/mod-email`,
|
|
4
|
+
built on [nodemailer](https://nodemailer.com) — any relay (SES, Postmark,
|
|
5
|
+
Mailgun, your provider's SMTP endpoint), your own server, or a local catcher
|
|
6
|
+
like [Mailpit](https://mailpit.axllent.org) in dev.
|
|
7
|
+
|
|
8
|
+
```jsonc
|
|
9
|
+
{ "mods": ["@pattern-js/mod-email", "@pattern-js/mod-email-smtp"] }
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Fields
|
|
13
|
+
|
|
14
|
+
Configured per account in admin → **System → Email**:
|
|
15
|
+
|
|
16
|
+
- **Option `host`** (required) — the SMTP server.
|
|
17
|
+
- **Option `port`** — default `587`.
|
|
18
|
+
- **Option `secure`** — `"true"` for implicit TLS (usually port 465);
|
|
19
|
+
leave unset/`"false"` for STARTTLS on 587, the common case.
|
|
20
|
+
- **Option `user`** — the username; leave empty for an unauthenticated relay.
|
|
21
|
+
- **Secret `pass`** — the password, by reference (vault name or env-var name
|
|
22
|
+
such as `SMTP_PASSWORD`); only needed when `user` is set.
|
|
23
|
+
|
|
24
|
+
Press **Test** after saving — a real email proves host, TLS mode, credentials
|
|
25
|
+
and From acceptance in one go.
|
|
26
|
+
|
|
27
|
+
## Dev tip: a local catcher
|
|
28
|
+
|
|
29
|
+
Run Mailpit (`docker run -p 1025:1025 -p 8025:8025 axllent/mailpit`), create a
|
|
30
|
+
`default` account with `host: localhost`, `port: 1025`, no user — and every
|
|
31
|
+
sign-in link lands in the catcher's inbox at `http://localhost:8025`. Real
|
|
32
|
+
delivery mechanics, zero real emails.
|
|
33
|
+
|
|
34
|
+
## Under the hood
|
|
35
|
+
|
|
36
|
+
nodemailer handles the protocol (STARTTLS vs implicit TLS, AUTH mechanisms,
|
|
37
|
+
MIME); transports pool connections and are cached per account config, and the
|
|
38
|
+
cache self-invalidates when credentials rotate. SMTP errors surface as the
|
|
39
|
+
run's error verbatim (`454 TLS required`, `535 authentication failed`, …).
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pattern-js/mod-email-smtp",
|
|
3
|
+
"version": "0.3.0-main.4a77893",
|
|
4
|
+
"description": "SMTP driver for @pattern-js/mod-email, built on nodemailer — any relay, your own server, or a local catcher. Host/port/user are account options in admin → System → Email; the password comes from the vault or an env var.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pattern",
|
|
7
|
+
"mod",
|
|
8
|
+
"email",
|
|
9
|
+
"smtp",
|
|
10
|
+
"nodemailer",
|
|
11
|
+
"driver"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"docs",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"nodemailer": "^8.0.1",
|
|
30
|
+
"zod": "^4.0.0",
|
|
31
|
+
"@pattern-js/core": "0.3.0-main.4a77893",
|
|
32
|
+
"@pattern-js/mod-email": "0.3.0-main.4a77893",
|
|
33
|
+
"@pattern-js/runtime-node": "0.3.0-main.4a77893"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"@types/nodemailer": "^8.0.1",
|
|
38
|
+
"typescript": "^5.6.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=22.5"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/makkr-studio/pattern.git",
|
|
46
|
+
"directory": "packages/mod-email-smtp"
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://github.com/makkr-studio/pattern#readme",
|
|
49
|
+
"bugs": "https://github.com/makkr-studio/pattern/issues",
|
|
50
|
+
"author": "Makkr Studio (https://github.com/makkr-studio)",
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc -p tsconfig.json",
|
|
53
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
54
|
+
}
|
|
55
|
+
}
|