sently 0.4.6 → 0.5.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/CHANGELOG.md +81 -0
- package/README.md +180 -43
- package/dist/adapters/bun.js +2 -183
- package/dist/adapters/bun.js.map +2 -2
- package/dist/adapters/cf.js +2 -77
- package/dist/adapters/cf.js.map +2 -2
- package/dist/adapters/deno.js +2 -72
- package/dist/adapters/deno.js.map +2 -2
- package/dist/adapters/node.js +2 -180
- package/dist/adapters/node.js.map +2 -2
- package/dist/auth/oauth2.js +2 -13
- package/dist/auth/oauth2.js.map +1 -1
- package/dist/chunk-32dj0v9g.js +5 -0
- package/dist/{chunk-794hc3m4.js.map → chunk-32dj0v9g.js.map} +2 -2
- package/dist/chunk-cvmk9h1r.js +4 -0
- package/dist/chunk-cvmk9h1r.js.map +10 -0
- package/dist/chunk-j5m37k1c.js +4 -0
- package/dist/{chunk-ym3zzv8b.js.map → chunk-j5m37k1c.js.map} +2 -2
- package/dist/chunk-kdqxhybf.js +4 -0
- package/dist/chunk-kdqxhybf.js.map +10 -0
- package/dist/chunk-m2njnxre.js +13 -0
- package/dist/{chunk-tymfm441.js.map → chunk-m2njnxre.js.map} +2 -2
- package/dist/chunk-nhec1319.js +5 -0
- package/dist/chunk-nhec1319.js.map +10 -0
- package/dist/chunk-nj3wmg69.js +3 -0
- package/dist/{chunk-7fqv71z1.js.map → chunk-nj3wmg69.js.map} +2 -2
- package/dist/chunk-ntqmn6pe.js +4 -0
- package/dist/chunk-ntqmn6pe.js.map +10 -0
- package/dist/chunk-pjzsvsm6.js +4 -0
- package/dist/chunk-pjzsvsm6.js.map +11 -0
- package/dist/chunk-rhade81v.js +4 -0
- package/dist/{chunk-v0bahtg2.js.map → chunk-rhade81v.js.map} +1 -1
- package/dist/chunk-zwm5bfa4.js +4 -0
- package/dist/chunk-zwm5bfa4.js.map +10 -0
- package/dist/core/idempotency-key.d.ts +6 -0
- package/dist/core/rate-limiter.d.ts +23 -0
- package/dist/core/smtp.js +2 -31
- package/dist/core/smtp.js.map +1 -1
- package/dist/core/types.d.ts +42 -0
- package/dist/detect.d.ts +2 -0
- package/dist/detect.js +2 -180
- package/dist/detect.js.map +4 -5
- package/dist/dkim.d.ts +21 -0
- package/dist/dkim.js +9 -0
- package/dist/dkim.js.map +10 -0
- package/dist/idempotency.d.ts +46 -0
- package/dist/idempotency.js +3 -0
- package/dist/idempotency.js.map +10 -0
- package/dist/index.d.ts +106 -16
- package/dist/index.js +115 -14
- package/dist/mailer.d.ts +17 -0
- package/dist/mailer.js +3 -0
- package/dist/mailer.js.map +9 -0
- package/dist/plugins/react.d.ts +35 -0
- package/dist/plugins/react.js +3 -0
- package/dist/plugins/react.js.map +10 -0
- package/dist/plugins/template.js +2 -28
- package/dist/plugins/template.js.map +2 -2
- package/dist/pool/pool.d.ts +1 -23
- package/dist/pool/pool.js +2 -16
- package/dist/pool/pool.js.map +5 -3
- package/dist/react.d.ts +20 -0
- package/dist/react.js +3 -0
- package/dist/react.js.map +9 -0
- package/dist/transports/brevo.js +2 -115
- package/dist/transports/brevo.js.map +2 -2
- package/dist/transports/mailgun.js +2 -119
- package/dist/transports/mailgun.js.map +2 -2
- package/dist/transports/postmark.js +2 -113
- package/dist/transports/postmark.js.map +2 -2
- package/dist/transports/preview.js +2 -72
- package/dist/transports/preview.js.map +2 -2
- package/dist/transports/resend.d.ts +16 -0
- package/dist/transports/resend.js +2 -109
- package/dist/transports/resend.js.map +3 -3
- package/dist/transports/retry.d.ts +2 -0
- package/dist/transports/retry.js +2 -78
- package/dist/transports/retry.js.map +3 -3
- package/dist/transports/sendgrid.d.ts +8 -0
- package/dist/transports/sendgrid.js +2 -132
- package/dist/transports/sendgrid.js.map +3 -3
- package/dist/transports/ses.js +5 -251
- package/dist/transports/ses.js.map +2 -2
- package/dist/transports/smtp.js +2 -26
- package/dist/transports/smtp.js.map +1 -1
- package/dist/webhooks/brevo.d.ts +3 -0
- package/dist/webhooks/mailgun.d.ts +13 -0
- package/dist/webhooks/postmark.d.ts +3 -0
- package/dist/webhooks/resend.d.ts +12 -0
- package/dist/webhooks/sendgrid.d.ts +3 -0
- package/dist/webhooks/ses.d.ts +6 -0
- package/dist/webhooks/timing-safe-equal.d.ts +9 -0
- package/dist/webhooks/types.d.ts +32 -0
- package/dist/webhooks.d.ts +26 -0
- package/dist/webhooks.js +3 -0
- package/dist/webhooks.js.map +9 -0
- package/package.json +45 -6
- package/dist/chunk-794hc3m4.js +0 -105
- package/dist/chunk-7fqv71z1.js +0 -251
- package/dist/chunk-f4c9ttmr.js +0 -154
- package/dist/chunk-f4c9ttmr.js.map +0 -11
- package/dist/chunk-mp5c9bfd.js +0 -270
- package/dist/chunk-mp5c9bfd.js.map +0 -11
- package/dist/chunk-tymfm441.js +0 -405
- package/dist/chunk-v0bahtg2.js +0 -6
- package/dist/chunk-x3szga4k.js +0 -367
- package/dist/chunk-x3szga4k.js.map +0 -11
- package/dist/chunk-ym3zzv8b.js +0 -74
package/dist/adapters/bun.js.map
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * @module\n * Bun socket adapter for SMTP connections via the Node.js compatibility layer.\n *\n * @example\n * ```ts\n * import { BunAdapter } from \"sently/adapters/bun\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * host: \"smtp.example.com\",\n * adapter: new BunAdapter(),\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport net from \"node:net\";\nimport tls from \"node:tls\";\nimport type { SocketAdapter, TLSOptions } from \"../core/types.js\";\n\nfunction warnRejectUnauthorizedDisabled(tls: TLSOptions): void {\n if (tls.rejectUnauthorized === false) {\n console.warn(\n \"[sently] TLS certificate verification is disabled. \" +\n \"Never use rejectUnauthorized: false in production.\",\n );\n }\n}\n\ndeclare const Bun: { version: string } | undefined;\n\n/** Configuration options for {@link BunAdapter}. */\nexport interface BunAdapterOptions {\n /** Use implicit TLS on connect (port 465). Default: false. */\n secure?: boolean;\n /** Socket connect timeout in milliseconds. Default: 30_000. */\n connectionTimeout?: number;\n /** TLS options passed to node:tls (Bun Node compat layer). */\n tls?: TLSOptions;\n}\n\n/**\n * Bun socket adapter using node:net and node:tls (Node compat layer).\n */\nexport class BunAdapter implements SocketAdapter {\n /** Underlying TCP or TLS socket (Node compat layer). */\n private socket: net.Socket | tls.TLSSocket | null = null;\n /** Whether the connection is currently encrypted. */\n private _secure: boolean;\n /** Whether the socket is connected. */\n private _connected = false;\n /** Socket connect timeout in milliseconds. */\n private readonly connectionTimeout: number;\n /** TLS options for direct TLS and STARTTLS upgrades. */\n private readonly tlsOptions: TLSOptions;\n\n /** Creates a Bun socket adapter (requires the Bun runtime). */\n constructor(options: BunAdapterOptions = {}) {\n if (typeof Bun === \"undefined\") {\n throw new Error(\"BunAdapter requires the Bun runtime\");\n }\n this._secure = options.secure ?? false;\n this.connectionTimeout = options.connectionTimeout ?? 30_000;\n this.tlsOptions = options.tls ?? {};\n }\n\n /** Whether the connection uses TLS. */\n get secure(): boolean {\n return this._secure;\n }\n\n /** Whether the socket is currently connected. */\n get connected(): boolean {\n return this._connected;\n }\n\n /** Opens a TCP or TLS connection to the given host and port. */\n async connect(host: string, port: number): Promise<void> {\n if (this._secure) {\n await this.connectTls(host, port);\n } else {\n await this.connectPlain(host, port);\n }\n this._connected = true;\n }\n\n /** Upgrades a plain connection to TLS via STARTTLS. */\n async startTLS(options?: TLSOptions): Promise<void> {\n if (!this.socket || this._secure) {\n throw new Error(\"Cannot STARTTLS: no plain socket available\");\n }\n\n const plain = this.socket;\n const merged = { ...this.tlsOptions, ...options };\n warnRejectUnauthorizedDisabled(merged);\n\n await new Promise<void>((resolve, reject) => {\n const tlsSocket = tls.connect({\n socket: plain,\n servername: merged.servername,\n rejectUnauthorized: merged.rejectUnauthorized ?? true,\n minVersion: merged.minVersion,\n });\n\n tlsSocket.once(\"secureConnect\", () => {\n this.socket = tlsSocket;\n this._secure = true;\n resolve();\n });\n tlsSocket.once(\"error\", reject);\n });\n }\n\n /** Writes raw bytes to the socket. */\n async write(data: Uint8Array): Promise<void> {\n if (!this.socket) {\n throw new Error(\"Socket not connected\");\n }\n\n await new Promise<void>((resolve, reject) => {\n this.socket?.write(Buffer.from(data), (err: Error | null | undefined) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n });\n }\n\n /** Reads incoming socket data as an async iterable of byte chunks. */\n async *read(): AsyncGenerator<Uint8Array, void, unknown> {\n if (!this.socket) {\n throw new Error(\"Socket not connected\");\n }\n\n const socket = this.socket;\n const queue: Uint8Array[] = [];\n let resolveNext: ((value: IteratorResult<Uint8Array>) => void) | null = null;\n let done = false;\n let error: Error | null = null;\n\n const onData = (chunk: Buffer): void => {\n const data = new Uint8Array(chunk);\n if (resolveNext) {\n resolveNext({ value: data, done: false });\n resolveNext = null;\n } else {\n queue.push(data);\n }\n };\n\n const onError = (err: Error): void => {\n error = err;\n done = true;\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Uint8Array, done: true });\n resolveNext = null;\n }\n };\n\n const onClose = (): void => {\n done = true;\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Uint8Array, done: true });\n resolveNext = null;\n }\n };\n\n socket.on(\"data\", onData);\n socket.on(\"error\", onError);\n socket.on(\"close\", onClose);\n\n try {\n while (!done || queue.length > 0) {\n if (error) {\n throw error;\n }\n if (queue.length > 0) {\n yield queue.shift() as Uint8Array;\n continue;\n }\n if (done) {\n break;\n }\n const chunk = await new Promise<IteratorResult<Uint8Array>>((resolve) => {\n resolveNext = resolve;\n });\n if (chunk.done) {\n break;\n }\n yield chunk.value;\n }\n } finally {\n socket.off(\"data\", onData);\n socket.off(\"error\", onError);\n socket.off(\"close\", onClose);\n }\n }\n\n /** Closes the socket connection. */\n async close(): Promise<void> {\n if (!this.socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n this.socket?.end(() => resolve());\n });\n this.socket = null;\n this._connected = false;\n }\n\n /** Opens a plain TCP connection to the SMTP server. */\n private connectPlain(host: string, port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = net.connect({ host, port }, () => resolve());\n socket.setTimeout(this.connectionTimeout);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new Error(\"Connection timeout\"));\n });\n socket.once(\"error\", reject);\n this.socket = socket;\n });\n }\n\n /** Opens a direct TLS connection to the SMTP server. */\n private connectTls(host: string, port: number): Promise<void> {\n warnRejectUnauthorizedDisabled(this.tlsOptions);\n return new Promise((resolve, reject) => {\n const socket = tls.connect(\n {\n host,\n port,\n servername: this.tlsOptions.servername ?? host,\n rejectUnauthorized: this.tlsOptions.rejectUnauthorized ?? true,\n minVersion: this.tlsOptions.minVersion,\n },\n () => resolve(),\n );\n socket.setTimeout(this.connectionTimeout);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new Error(\"Connection timeout\"));\n });\n socket.once(\"error\", reject);\n this.socket = socket;\n });\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "94AAFD676203266764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/adapters/cf.js
CHANGED
|
@@ -1,78 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__require
|
|
3
|
-
} from "../chunk-v0bahtg2.js";
|
|
1
|
+
import{K as q}from"../chunk-rhade81v.js";class w{socket=null;writer=null;_secure;_connected=!1;directTls;starttls;constructor(b={}){this._secure=b.secure??!1,this.directTls=b.secure??!1,this.starttls=b.starttls??!this.directTls}get secure(){return this._secure}get connected(){return this._connected}async connect(b,g){let{connect:j}=await import("cloudflare:sockets"),m=this.directTls?"on":this.starttls?"starttls":"off";this.socket=j({hostname:b,port:g},{secureTransport:m}),this.writer=this.socket.writable.getWriter(),this._connected=!0,this._secure=m==="on"}async startTLS(b){if(!this.socket||this._secure)throw Error("Cannot STARTTLS: no plain socket available");await this.writer?.close(),this.socket=this.socket.startTls(),this.writer=this.socket.writable.getWriter(),this._secure=!0}async write(b){if(!this.writer)throw Error("Socket not connected");await this.writer.write(b)}async*read(){if(!this.socket)throw Error("Socket not connected");let b=this.socket.readable.getReader();try{while(!0){let{value:g,done:j}=await b.read();if(j)break;if(g)yield g}}finally{b.releaseLock()}}async close(){await this.writer?.close(),await this.socket?.close(),this.writer=null,this.socket=null,this._connected=!1}}export{w as CloudflareAdapter};
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
class CloudflareAdapter {
|
|
7
|
-
socket = null;
|
|
8
|
-
writer = null;
|
|
9
|
-
_secure;
|
|
10
|
-
_connected = false;
|
|
11
|
-
directTls;
|
|
12
|
-
starttls;
|
|
13
|
-
constructor(options = {}) {
|
|
14
|
-
this._secure = options.secure ?? false;
|
|
15
|
-
this.directTls = options.secure ?? false;
|
|
16
|
-
this.starttls = options.starttls ?? !this.directTls;
|
|
17
|
-
}
|
|
18
|
-
get secure() {
|
|
19
|
-
return this._secure;
|
|
20
|
-
}
|
|
21
|
-
get connected() {
|
|
22
|
-
return this._connected;
|
|
23
|
-
}
|
|
24
|
-
async connect(host, port) {
|
|
25
|
-
const { connect } = await import("cloudflare:sockets");
|
|
26
|
-
const secureTransport = this.directTls ? "on" : this.starttls ? "starttls" : "off";
|
|
27
|
-
this.socket = connect({ hostname: host, port }, { secureTransport });
|
|
28
|
-
this.writer = this.socket.writable.getWriter();
|
|
29
|
-
this._connected = true;
|
|
30
|
-
this._secure = secureTransport === "on";
|
|
31
|
-
}
|
|
32
|
-
async startTLS(_options) {
|
|
33
|
-
if (!this.socket || this._secure) {
|
|
34
|
-
throw new Error("Cannot STARTTLS: no plain socket available");
|
|
35
|
-
}
|
|
36
|
-
await this.writer?.close();
|
|
37
|
-
this.socket = this.socket.startTls();
|
|
38
|
-
this.writer = this.socket.writable.getWriter();
|
|
39
|
-
this._secure = true;
|
|
40
|
-
}
|
|
41
|
-
async write(data) {
|
|
42
|
-
if (!this.writer) {
|
|
43
|
-
throw new Error("Socket not connected");
|
|
44
|
-
}
|
|
45
|
-
await this.writer.write(data);
|
|
46
|
-
}
|
|
47
|
-
async* read() {
|
|
48
|
-
if (!this.socket) {
|
|
49
|
-
throw new Error("Socket not connected");
|
|
50
|
-
}
|
|
51
|
-
const reader = this.socket.readable.getReader();
|
|
52
|
-
try {
|
|
53
|
-
while (true) {
|
|
54
|
-
const { value, done } = await reader.read();
|
|
55
|
-
if (done) {
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
if (value) {
|
|
59
|
-
yield value;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
} finally {
|
|
63
|
-
reader.releaseLock();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
async close() {
|
|
67
|
-
await this.writer?.close();
|
|
68
|
-
await this.socket?.close();
|
|
69
|
-
this.writer = null;
|
|
70
|
-
this.socket = null;
|
|
71
|
-
this._connected = false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
export {
|
|
75
|
-
CloudflareAdapter
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
//# debugId=035AC57F8987550264756E2164756E21
|
|
3
|
+
//# debugId=1AA656504F14311964756E2164756E21
|
package/dist/adapters/cf.js.map
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * @module\n * Cloudflare Workers socket adapter for SMTP via cloudflare:sockets.\n *\n * @example\n * ```ts\n * import { CloudflareAdapter } from \"sently/adapters/cf\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * host: \"smtp.example.com\",\n * adapter: new CloudflareAdapter(),\n * auth: { user: \"relay@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport type { SocketAdapter, TLSOptions } from \"../core/types.js\";\n\ninterface CFSocket {\n readonly readable: ReadableStream<Uint8Array>;\n readonly writable: WritableStream<Uint8Array>;\n readonly closed: Promise<void>;\n close(): Promise<void>;\n startTls(): CFSocket;\n}\n\ntype CFConnect = (\n address: { hostname: string; port: number },\n options?: { secureTransport?: \"off\" | \"on\" | \"starttls\"; allowHalfOpen?: boolean },\n) => CFSocket;\n\n/** Configuration options for {@link CloudflareAdapter}. */\nexport interface CloudflareAdapterOptions {\n /** Use implicit TLS on connect (secureTransport: \"on\"). Default: false. */\n secure?: boolean;\n /** Enable STARTTLS upgrade after plain connect. Default: true when not secure. */\n starttls?: boolean;\n /** Reserved for future TLS tuning (Workers sockets API). */\n tls?: TLSOptions;\n}\n\n/**\n * Cloudflare Workers socket adapter via cloudflare:sockets.\n *\n * Limitations:\n * - No connection pooling (isolate lifecycle)\n * - No file system access for attachment.path\n * - No DNS MX lookup — explicit SMTP relay host required\n */\nexport class CloudflareAdapter implements SocketAdapter {\n /** Underlying Cloudflare Workers socket. */\n private socket: CFSocket | null = null;\n /** Writable stream writer for outbound bytes. */\n private writer: WritableStreamDefaultWriter<Uint8Array> | null = null;\n /** Whether the connection is currently encrypted. */\n private _secure: boolean;\n /** Whether the socket is connected. */\n private _connected = false;\n /** Whether implicit TLS is used on connect (port 465 style). */\n private readonly directTls: boolean;\n /** Whether STARTTLS upgrade is enabled after plain connect. */\n private readonly starttls: boolean;\n\n /** Creates a Cloudflare Workers socket adapter. */\n constructor(options: CloudflareAdapterOptions = {}) {\n this._secure = options.secure ?? false;\n this.directTls = options.secure ?? false;\n this.starttls = options.starttls ?? !this.directTls;\n }\n\n /** Whether the connection uses TLS. */\n get secure(): boolean {\n return this._secure;\n }\n\n /** Whether the socket is currently connected. */\n get connected(): boolean {\n return this._connected;\n }\n\n /** Opens a TCP or TLS connection to the given host and port. */\n async connect(host: string, port: number): Promise<void> {\n const { connect } = (await import(\"cloudflare:sockets\")) as { connect: CFConnect };\n\n const secureTransport = this.directTls ? \"on\" : this.starttls ? \"starttls\" : \"off\";\n this.socket = connect({ hostname: host, port }, { secureTransport });\n this.writer = this.socket.writable.getWriter();\n this._connected = true;\n this._secure = secureTransport === \"on\";\n }\n\n /** Upgrades a plain connection to TLS via STARTTLS. */\n async startTLS(_options?: TLSOptions): Promise<void> {\n if (!this.socket || this._secure) {\n throw new Error(\"Cannot STARTTLS: no plain socket available\");\n }\n\n await this.writer?.close();\n this.socket = this.socket.startTls();\n this.writer = this.socket.writable.getWriter();\n this._secure = true;\n }\n\n /** Writes raw bytes to the socket. */\n async write(data: Uint8Array): Promise<void> {\n if (!this.writer) {\n throw new Error(\"Socket not connected\");\n }\n await this.writer.write(data);\n }\n\n /** Reads incoming socket data as an async iterable of byte chunks. */\n async *read(): AsyncGenerator<Uint8Array, void, unknown> {\n if (!this.socket) {\n throw new Error(\"Socket not connected\");\n }\n\n const reader = this.socket.readable.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n break;\n }\n if (value) {\n yield value;\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /** Closes the socket connection. */\n async close(): Promise<void> {\n await this.writer?.close();\n await this.socket?.close();\n this.writer = null;\n this.socket = null;\n this._connected = false;\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "yCAiDO,MAAM,CAA2C,CAE9C,OAA0B,KAE1B,OAAyD,KAEzD,QAEA,WAAa,GAEJ,UAEA,SAGjB,WAAW,CAAC,EAAoC,CAAC,EAAG,CAClD,KAAK,QAAU,EAAQ,QAAU,GACjC,KAAK,UAAY,EAAQ,QAAU,GACnC,KAAK,SAAW,EAAQ,UAAY,CAAC,KAAK,aAIxC,OAAM,EAAY,CACpB,OAAO,KAAK,WAIV,UAAS,EAAY,CACvB,OAAO,KAAK,gBAIR,QAAO,CAAC,EAAc,EAA6B,CACvD,IAAQ,WAAa,KAAa,8BAE5B,EAAkB,KAAK,UAAY,KAAO,KAAK,SAAW,WAAa,MAC7E,KAAK,OAAS,EAAQ,CAAE,SAAU,EAAM,MAAK,EAAG,CAAE,iBAAgB,CAAC,EACnE,KAAK,OAAS,KAAK,OAAO,SAAS,UAAU,EAC7C,KAAK,WAAa,GAClB,KAAK,QAAU,IAAoB,UAI/B,SAAQ,CAAC,EAAsC,CACnD,GAAI,CAAC,KAAK,QAAU,KAAK,QACvB,MAAU,MAAM,4CAA4C,EAG9D,MAAM,KAAK,QAAQ,MAAM,EACzB,KAAK,OAAS,KAAK,OAAO,SAAS,EACnC,KAAK,OAAS,KAAK,OAAO,SAAS,UAAU,EAC7C,KAAK,QAAU,QAIX,MAAK,CAAC,EAAiC,CAC3C,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAExC,MAAM,KAAK,OAAO,MAAM,CAAI,QAIvB,IAAI,EAA8C,CACvD,GAAI,CAAC,KAAK,OACR,MAAU,MAAM,sBAAsB,EAGxC,IAAM,EAAS,KAAK,OAAO,SAAS,UAAU,EAC9C,GAAI,CACF,MAAO,GAAM,CACX,IAAQ,QAAO,QAAS,MAAM,EAAO,KAAK,EAC1C,GAAI,EACF,MAEF,GAAI,EACF,MAAM,UAGV,CACA,EAAO,YAAY,QAKjB,MAAK,EAAkB,CAC3B,MAAM,KAAK,QAAQ,MAAM,EACzB,MAAM,KAAK,QAAQ,MAAM,EACzB,KAAK,OAAS,KACd,KAAK,OAAS,KACd,KAAK,WAAa,GAEtB",
|
|
8
|
+
"debugId": "1AA656504F14311964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/adapters/deno.js
CHANGED
|
@@ -1,73 +1,3 @@
|
|
|
1
|
-
import"../chunk-
|
|
1
|
+
import"../chunk-rhade81v.js";class v{conn=null;_secure;_connected=!1;tlsOptions;constructor(j={}){if(typeof Deno>"u")throw Error("DenoAdapter requires the Deno runtime");this._secure=j.secure??!1,this.tlsOptions=j.tls??{}}get secure(){return this._secure}get connected(){return this._connected}async connect(j,q){if(this._secure)this.conn=await Deno.connectTls({hostname:j,port:q,...this.tlsOptions.servername?{servername:this.tlsOptions.servername}:{}});else this.conn=await Deno.connect({hostname:j,port:q});this._connected=!0}async startTLS(j){if(!this.conn||this._secure)throw Error("Cannot STARTTLS: no plain connection available");let q={...this.tlsOptions,...j};this.conn=await Deno.startTls(this.conn,{...q.servername?{hostname:q.servername}:{}}),this._secure=!0}async write(j){if(!this.conn)throw Error("Socket not connected");await this.conn.write(j)}async*read(){if(!this.conn)throw Error("Socket not connected");let j=new Uint8Array(8192);while(!0){let q=await this.conn.read(j);if(q===null)break;yield j.slice(0,q)}}async close(){this.conn?.close(),this.conn=null,this._connected=!1}}export{v as DenoAdapter};
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
class DenoAdapter {
|
|
5
|
-
conn = null;
|
|
6
|
-
_secure;
|
|
7
|
-
_connected = false;
|
|
8
|
-
tlsOptions;
|
|
9
|
-
constructor(options = {}) {
|
|
10
|
-
if (typeof Deno === "undefined") {
|
|
11
|
-
throw new Error("DenoAdapter requires the Deno runtime");
|
|
12
|
-
}
|
|
13
|
-
this._secure = options.secure ?? false;
|
|
14
|
-
this.tlsOptions = options.tls ?? {};
|
|
15
|
-
}
|
|
16
|
-
get secure() {
|
|
17
|
-
return this._secure;
|
|
18
|
-
}
|
|
19
|
-
get connected() {
|
|
20
|
-
return this._connected;
|
|
21
|
-
}
|
|
22
|
-
async connect(host, port) {
|
|
23
|
-
if (this._secure) {
|
|
24
|
-
this.conn = await Deno.connectTls({
|
|
25
|
-
hostname: host,
|
|
26
|
-
port,
|
|
27
|
-
...this.tlsOptions.servername ? { servername: this.tlsOptions.servername } : {}
|
|
28
|
-
});
|
|
29
|
-
} else {
|
|
30
|
-
this.conn = await Deno.connect({ hostname: host, port });
|
|
31
|
-
}
|
|
32
|
-
this._connected = true;
|
|
33
|
-
}
|
|
34
|
-
async startTLS(options) {
|
|
35
|
-
if (!this.conn || this._secure) {
|
|
36
|
-
throw new Error("Cannot STARTTLS: no plain connection available");
|
|
37
|
-
}
|
|
38
|
-
const merged = { ...this.tlsOptions, ...options };
|
|
39
|
-
this.conn = await Deno.startTls(this.conn, {
|
|
40
|
-
...merged.servername ? { hostname: merged.servername } : {}
|
|
41
|
-
});
|
|
42
|
-
this._secure = true;
|
|
43
|
-
}
|
|
44
|
-
async write(data) {
|
|
45
|
-
if (!this.conn) {
|
|
46
|
-
throw new Error("Socket not connected");
|
|
47
|
-
}
|
|
48
|
-
await this.conn.write(data);
|
|
49
|
-
}
|
|
50
|
-
async* read() {
|
|
51
|
-
if (!this.conn) {
|
|
52
|
-
throw new Error("Socket not connected");
|
|
53
|
-
}
|
|
54
|
-
const buffer = new Uint8Array(8192);
|
|
55
|
-
while (true) {
|
|
56
|
-
const n = await this.conn.read(buffer);
|
|
57
|
-
if (n === null) {
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
yield buffer.slice(0, n);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
async close() {
|
|
64
|
-
this.conn?.close();
|
|
65
|
-
this.conn = null;
|
|
66
|
-
this._connected = false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
export {
|
|
70
|
-
DenoAdapter
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
//# debugId=871B95B1C9304E4564756E2164756E21
|
|
3
|
+
//# debugId=433F0358DDA6910C64756E2164756E21
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * @module\n * Deno socket adapter for SMTP connections via Deno.connect and Deno.startTls.\n *\n * @example\n * ```ts\n * import { DenoAdapter } from \"sently/adapters/deno\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * host: \"smtp.example.com\",\n * adapter: new DenoAdapter(),\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport type { SocketAdapter, TLSOptions } from \"../core/types.js\";\n\ndeclare const Deno: {\n connect(options: { hostname: string; port: number }): Promise<DenoTcpConn>;\n connectTls(options: {\n hostname: string;\n port: number;\n [key: string]: unknown;\n }): Promise<DenoTlsConn>;\n startTls(conn: DenoTcpConn, options?: { hostname?: string }): Promise<DenoTlsConn>;\n};\n\ninterface DenoConn {\n read(p: Uint8Array): Promise<number | null>;\n write(p: Uint8Array): Promise<number>;\n close(): void;\n}\n\ninterface DenoTcpConn extends DenoConn {}\ninterface DenoTlsConn extends DenoConn {}\n\n/** Configuration options for {@link DenoAdapter}. */\nexport interface DenoAdapterOptions {\n /** Use implicit TLS on connect (port 465). Default: false. */\n secure?: boolean;\n /** Socket connect timeout in milliseconds. Default: 30_000. */\n connectionTimeout?: number;\n /** TLS options passed to Deno.startTls / Deno.connectTls. */\n tls?: TLSOptions;\n}\n\n/**\n * Deno socket adapter using Deno.connect / Deno.startTls.\n */\nexport class DenoAdapter implements SocketAdapter {\n /** Active Deno TCP or TLS connection. */\n private conn: DenoConn | null = null;\n /** Whether the connection is currently encrypted. */\n private _secure: boolean;\n /** Whether the socket is connected. */\n private _connected = false;\n /** TLS options for direct TLS and STARTTLS upgrades. */\n private readonly tlsOptions: TLSOptions;\n\n /** Creates a Deno socket adapter (requires the Deno runtime). */\n constructor(options: DenoAdapterOptions = {}) {\n if (typeof Deno === \"undefined\") {\n throw new Error(\"DenoAdapter requires the Deno runtime\");\n }\n this._secure = options.secure ?? false;\n this.tlsOptions = options.tls ?? {};\n }\n\n /** Whether the connection uses TLS. */\n get secure(): boolean {\n return this._secure;\n }\n\n /** Whether the socket is currently connected. */\n get connected(): boolean {\n return this._connected;\n }\n\n /** Opens a TCP or TLS connection to the given host and port. */\n async connect(host: string, port: number): Promise<void> {\n if (this._secure) {\n this.conn = await Deno.connectTls({\n hostname: host,\n port,\n ...(this.tlsOptions.servername ? { servername: this.tlsOptions.servername } : {}),\n });\n } else {\n this.conn = await Deno.connect({ hostname: host, port });\n }\n this._connected = true;\n }\n\n /** Upgrades a plain connection to TLS via STARTTLS. */\n async startTLS(options?: TLSOptions): Promise<void> {\n if (!this.conn || this._secure) {\n throw new Error(\"Cannot STARTTLS: no plain connection available\");\n }\n\n const merged = { ...this.tlsOptions, ...options };\n this.conn = await Deno.startTls(this.conn as DenoTcpConn, {\n ...(merged.servername ? { hostname: merged.servername } : {}),\n });\n this._secure = true;\n }\n\n /** Writes raw bytes to the socket. */\n async write(data: Uint8Array): Promise<void> {\n if (!this.conn) {\n throw new Error(\"Socket not connected\");\n }\n await this.conn.write(data);\n }\n\n /** Reads incoming socket data as an async iterable of byte chunks. */\n async *read(): AsyncGenerator<Uint8Array, void, unknown> {\n if (!this.conn) {\n throw new Error(\"Socket not connected\");\n }\n\n const buffer = new Uint8Array(8192);\n while (true) {\n const n = await this.conn.read(buffer);\n if (n === null) {\n break;\n }\n yield buffer.slice(0, n);\n }\n }\n\n /** Closes the socket connection. */\n async close(): Promise<void> {\n this.conn?.close();\n this.conn = null;\n this._connected = false;\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "6BAkDO,MAAM,CAAqC,CAExC,KAAwB,KAExB,QAEA,WAAa,GAEJ,WAGjB,WAAW,CAAC,EAA8B,CAAC,EAAG,CAC5C,GAAI,OAAO,KAAS,IAClB,MAAU,MAAM,uCAAuC,EAEzD,KAAK,QAAU,EAAQ,QAAU,GACjC,KAAK,WAAa,EAAQ,KAAO,CAAC,KAIhC,OAAM,EAAY,CACpB,OAAO,KAAK,WAIV,UAAS,EAAY,CACvB,OAAO,KAAK,gBAIR,QAAO,CAAC,EAAc,EAA6B,CACvD,GAAI,KAAK,QACP,KAAK,KAAO,MAAM,KAAK,WAAW,CAChC,SAAU,EACV,UACI,KAAK,WAAW,WAAa,CAAE,WAAY,KAAK,WAAW,UAAW,EAAI,CAAC,CACjF,CAAC,EAED,UAAK,KAAO,MAAM,KAAK,QAAQ,CAAE,SAAU,EAAM,MAAK,CAAC,EAEzD,KAAK,WAAa,QAId,SAAQ,CAAC,EAAqC,CAClD,GAAI,CAAC,KAAK,MAAQ,KAAK,QACrB,MAAU,MAAM,gDAAgD,EAGlE,IAAM,EAAS,IAAK,KAAK,cAAe,CAAQ,EAChD,KAAK,KAAO,MAAM,KAAK,SAAS,KAAK,KAAqB,IACpD,EAAO,WAAa,CAAE,SAAU,EAAO,UAAW,EAAI,CAAC,CAC7D,CAAC,EACD,KAAK,QAAU,QAIX,MAAK,CAAC,EAAiC,CAC3C,GAAI,CAAC,KAAK,KACR,MAAU,MAAM,sBAAsB,EAExC,MAAM,KAAK,KAAK,MAAM,CAAI,QAIrB,IAAI,EAA8C,CACvD,GAAI,CAAC,KAAK,KACR,MAAU,MAAM,sBAAsB,EAGxC,IAAM,EAAS,IAAI,WAAW,IAAI,EAClC,MAAO,GAAM,CACX,IAAM,EAAI,MAAM,KAAK,KAAK,KAAK,CAAM,EACrC,GAAI,IAAM,KACR,MAEF,MAAM,EAAO,MAAM,EAAG,CAAC,QAKrB,MAAK,EAAkB,CAC3B,KAAK,MAAM,MAAM,EACjB,KAAK,KAAO,KACZ,KAAK,WAAa,GAEtB",
|
|
8
|
+
"debugId": "433F0358DDA6910C64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/adapters/node.js
CHANGED
|
@@ -1,181 +1,3 @@
|
|
|
1
|
-
import"../chunk-
|
|
1
|
+
import"../chunk-rhade81v.js";import W from"node:net";import Q from"node:tls";function V(f){if(f.rejectUnauthorized===!1)console.warn("[sently] TLS certificate verification is disabled. Never use rejectUnauthorized: false in production.")}class X{socket=null;_secure;_connected=!1;connectionTimeout;tlsOptions;constructor(f={}){this._secure=f.secure??!1,this.connectionTimeout=f.connectionTimeout??30000,this.tlsOptions=f.tls??{}}get secure(){return this._secure}get connected(){return this._connected}async connect(f,B){if(this._secure)await this.connectTls(f,B);else await this.connectPlain(f,B);this._connected=!0}async startTLS(f){if(!this.socket||this._secure)throw Error("Cannot STARTTLS: no plain socket available");let B=this.socket,y={...this.tlsOptions,...f};V(y),await new Promise((F,A)=>{let G=Q.connect({socket:B,servername:y.servername,rejectUnauthorized:y.rejectUnauthorized??!0,minVersion:y.minVersion});G.once("secureConnect",()=>{this.socket=G,this._secure=!0,F()}),G.once("error",A)})}async write(f){if(!this.socket)throw Error("Socket not connected");await new Promise((B,y)=>{this.socket?.write(Buffer.from(f),(F)=>{if(F)y(F);else B()})})}async*read(){if(!this.socket)throw Error("Socket not connected");let f=this.socket,B=[],y=null,F=!1,A=null,G=(H)=>{let I=new Uint8Array(H);if(y)y({value:I,done:!1}),y=null;else B.push(I)},J=(H)=>{if(A=H,y)y({value:void 0,done:!0}),y=null;F=!0},K=()=>{if(F=!0,y)y({value:void 0,done:!0}),y=null};f.on("data",G),f.on("error",J),f.on("close",K);try{while(!F||B.length>0){if(A)throw A;if(B.length>0){yield B.shift();continue}if(F)break;let H=await new Promise((I)=>{y=I});if(H.done)break;yield H.value}}finally{f.off("data",G),f.off("error",J),f.off("close",K)}}async close(){if(!this.socket)return;await new Promise((f)=>{this.socket?.end(()=>f())}),this.socket=null,this._connected=!1}connectPlain(f,B){return new Promise((y,F)=>{let A=W.connect({host:f,port:B},()=>y());A.setTimeout(this.connectionTimeout),A.once("timeout",()=>{A.destroy(),F(Error("Connection timeout"))}),A.once("error",F),this.socket=A})}connectTls(f,B){return V(this.tlsOptions),new Promise((y,F)=>{let A=Q.connect({host:f,port:B,servername:this.tlsOptions.servername??f,rejectUnauthorized:this.tlsOptions.rejectUnauthorized??!0,minVersion:this.tlsOptions.minVersion},()=>y());A.setTimeout(this.connectionTimeout),A.once("timeout",()=>{A.destroy(),F(Error("Connection timeout"))}),A.once("error",F),this.socket=A})}}export{X as NodeAdapter};
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import net from "node:net";
|
|
5
|
-
import tls from "node:tls";
|
|
6
|
-
function warnRejectUnauthorizedDisabled(tls2) {
|
|
7
|
-
if (tls2.rejectUnauthorized === false) {
|
|
8
|
-
console.warn("[sently] TLS certificate verification is disabled. " + "Never use rejectUnauthorized: false in production.");
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class NodeAdapter {
|
|
13
|
-
socket = null;
|
|
14
|
-
_secure;
|
|
15
|
-
_connected = false;
|
|
16
|
-
connectionTimeout;
|
|
17
|
-
tlsOptions;
|
|
18
|
-
constructor(options = {}) {
|
|
19
|
-
this._secure = options.secure ?? false;
|
|
20
|
-
this.connectionTimeout = options.connectionTimeout ?? 30000;
|
|
21
|
-
this.tlsOptions = options.tls ?? {};
|
|
22
|
-
}
|
|
23
|
-
get secure() {
|
|
24
|
-
return this._secure;
|
|
25
|
-
}
|
|
26
|
-
get connected() {
|
|
27
|
-
return this._connected;
|
|
28
|
-
}
|
|
29
|
-
async connect(host, port) {
|
|
30
|
-
if (this._secure) {
|
|
31
|
-
await this.connectTls(host, port);
|
|
32
|
-
} else {
|
|
33
|
-
await this.connectPlain(host, port);
|
|
34
|
-
}
|
|
35
|
-
this._connected = true;
|
|
36
|
-
}
|
|
37
|
-
async startTLS(options) {
|
|
38
|
-
if (!this.socket || this._secure) {
|
|
39
|
-
throw new Error("Cannot STARTTLS: no plain socket available");
|
|
40
|
-
}
|
|
41
|
-
const plain = this.socket;
|
|
42
|
-
const merged = { ...this.tlsOptions, ...options };
|
|
43
|
-
warnRejectUnauthorizedDisabled(merged);
|
|
44
|
-
await new Promise((resolve, reject) => {
|
|
45
|
-
const tlsSocket = tls.connect({
|
|
46
|
-
socket: plain,
|
|
47
|
-
servername: merged.servername,
|
|
48
|
-
rejectUnauthorized: merged.rejectUnauthorized ?? true,
|
|
49
|
-
minVersion: merged.minVersion
|
|
50
|
-
});
|
|
51
|
-
tlsSocket.once("secureConnect", () => {
|
|
52
|
-
this.socket = tlsSocket;
|
|
53
|
-
this._secure = true;
|
|
54
|
-
resolve();
|
|
55
|
-
});
|
|
56
|
-
tlsSocket.once("error", reject);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
async write(data) {
|
|
60
|
-
if (!this.socket) {
|
|
61
|
-
throw new Error("Socket not connected");
|
|
62
|
-
}
|
|
63
|
-
await new Promise((resolve, reject) => {
|
|
64
|
-
this.socket?.write(Buffer.from(data), (err) => {
|
|
65
|
-
if (err) {
|
|
66
|
-
reject(err);
|
|
67
|
-
} else {
|
|
68
|
-
resolve();
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
async* read() {
|
|
74
|
-
if (!this.socket) {
|
|
75
|
-
throw new Error("Socket not connected");
|
|
76
|
-
}
|
|
77
|
-
const socket = this.socket;
|
|
78
|
-
const queue = [];
|
|
79
|
-
let resolveNext = null;
|
|
80
|
-
let done = false;
|
|
81
|
-
let error = null;
|
|
82
|
-
const onData = (chunk) => {
|
|
83
|
-
const data = new Uint8Array(chunk);
|
|
84
|
-
if (resolveNext) {
|
|
85
|
-
resolveNext({ value: data, done: false });
|
|
86
|
-
resolveNext = null;
|
|
87
|
-
} else {
|
|
88
|
-
queue.push(data);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
const onError = (err) => {
|
|
92
|
-
error = err;
|
|
93
|
-
if (resolveNext) {
|
|
94
|
-
resolveNext({ value: undefined, done: true });
|
|
95
|
-
resolveNext = null;
|
|
96
|
-
}
|
|
97
|
-
done = true;
|
|
98
|
-
};
|
|
99
|
-
const onClose = () => {
|
|
100
|
-
done = true;
|
|
101
|
-
if (resolveNext) {
|
|
102
|
-
resolveNext({ value: undefined, done: true });
|
|
103
|
-
resolveNext = null;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
socket.on("data", onData);
|
|
107
|
-
socket.on("error", onError);
|
|
108
|
-
socket.on("close", onClose);
|
|
109
|
-
try {
|
|
110
|
-
while (!done || queue.length > 0) {
|
|
111
|
-
if (error) {
|
|
112
|
-
throw error;
|
|
113
|
-
}
|
|
114
|
-
if (queue.length > 0) {
|
|
115
|
-
yield queue.shift();
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (done) {
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
const chunk = await new Promise((resolve) => {
|
|
122
|
-
resolveNext = resolve;
|
|
123
|
-
});
|
|
124
|
-
if (chunk.done) {
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
yield chunk.value;
|
|
128
|
-
}
|
|
129
|
-
} finally {
|
|
130
|
-
socket.off("data", onData);
|
|
131
|
-
socket.off("error", onError);
|
|
132
|
-
socket.off("close", onClose);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async close() {
|
|
136
|
-
if (!this.socket) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
await new Promise((resolve) => {
|
|
140
|
-
this.socket?.end(() => resolve());
|
|
141
|
-
});
|
|
142
|
-
this.socket = null;
|
|
143
|
-
this._connected = false;
|
|
144
|
-
}
|
|
145
|
-
connectPlain(host, port) {
|
|
146
|
-
return new Promise((resolve, reject) => {
|
|
147
|
-
const socket = net.connect({ host, port }, () => resolve());
|
|
148
|
-
socket.setTimeout(this.connectionTimeout);
|
|
149
|
-
socket.once("timeout", () => {
|
|
150
|
-
socket.destroy();
|
|
151
|
-
reject(new Error("Connection timeout"));
|
|
152
|
-
});
|
|
153
|
-
socket.once("error", reject);
|
|
154
|
-
this.socket = socket;
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
connectTls(host, port) {
|
|
158
|
-
warnRejectUnauthorizedDisabled(this.tlsOptions);
|
|
159
|
-
return new Promise((resolve, reject) => {
|
|
160
|
-
const socket = tls.connect({
|
|
161
|
-
host,
|
|
162
|
-
port,
|
|
163
|
-
servername: this.tlsOptions.servername ?? host,
|
|
164
|
-
rejectUnauthorized: this.tlsOptions.rejectUnauthorized ?? true,
|
|
165
|
-
minVersion: this.tlsOptions.minVersion
|
|
166
|
-
}, () => resolve());
|
|
167
|
-
socket.setTimeout(this.connectionTimeout);
|
|
168
|
-
socket.once("timeout", () => {
|
|
169
|
-
socket.destroy();
|
|
170
|
-
reject(new Error("Connection timeout"));
|
|
171
|
-
});
|
|
172
|
-
socket.once("error", reject);
|
|
173
|
-
this.socket = socket;
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
export {
|
|
178
|
-
NodeAdapter
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
//# debugId=ECC754A0A0C9909F64756E2164756E21
|
|
3
|
+
//# debugId=81CB75805F76C9AC64756E2164756E21
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * @module\n * Node.js socket adapter for SMTP connections via node:net and node:tls.\n *\n * @example\n * ```ts\n * import { NodeAdapter } from \"sently/adapters/node\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * host: \"smtp.example.com\",\n * adapter: new NodeAdapter(),\n * auth: { user: \"you@example.com\", pass: \"secret\" },\n * });\n * ```\n */\nimport net from \"node:net\";\nimport tls from \"node:tls\";\nimport type { SocketAdapter, TLSOptions } from \"../core/types.js\";\n\nfunction warnRejectUnauthorizedDisabled(tls: TLSOptions): void {\n if (tls.rejectUnauthorized === false) {\n console.warn(\n \"[sently] TLS certificate verification is disabled. \" +\n \"Never use rejectUnauthorized: false in production.\",\n );\n }\n}\n\n/** Configuration options for {@link NodeAdapter}. */\nexport interface NodeAdapterOptions {\n /** Use implicit TLS on connect (port 465). Default: false. */\n secure?: boolean;\n /** Socket connect timeout in milliseconds. Default: 30_000. */\n connectionTimeout?: number;\n /** TLS options passed to node:tls. */\n tls?: TLSOptions;\n}\n\n/**\n * Node.js socket adapter using node:net and node:tls.\n */\nexport class NodeAdapter implements SocketAdapter {\n /** Underlying Node.js TCP or TLS socket. */\n private socket: net.Socket | tls.TLSSocket | null = null;\n /** Whether the connection is currently encrypted. */\n private _secure: boolean;\n /** Whether the socket is connected. */\n private _connected = false;\n /** Socket connect timeout in milliseconds. */\n private readonly connectionTimeout: number;\n /** TLS options for direct TLS and STARTTLS upgrades. */\n private readonly tlsOptions: TLSOptions;\n\n /** Creates a Node.js socket adapter. */\n constructor(options: NodeAdapterOptions = {}) {\n this._secure = options.secure ?? false;\n this.connectionTimeout = options.connectionTimeout ?? 30_000;\n this.tlsOptions = options.tls ?? {};\n }\n\n /** Whether the connection uses TLS. */\n get secure(): boolean {\n return this._secure;\n }\n\n /** Whether the socket is currently connected. */\n get connected(): boolean {\n return this._connected;\n }\n\n /** Opens a TCP or TLS connection to the given host and port. */\n async connect(host: string, port: number): Promise<void> {\n if (this._secure) {\n await this.connectTls(host, port);\n } else {\n await this.connectPlain(host, port);\n }\n this._connected = true;\n }\n\n /** Upgrades a plain connection to TLS via STARTTLS. */\n async startTLS(options?: TLSOptions): Promise<void> {\n if (!this.socket || this._secure) {\n throw new Error(\"Cannot STARTTLS: no plain socket available\");\n }\n\n const plain = this.socket;\n const merged = { ...this.tlsOptions, ...options };\n warnRejectUnauthorizedDisabled(merged);\n\n await new Promise<void>((resolve, reject) => {\n const tlsSocket = tls.connect({\n socket: plain,\n servername: merged.servername,\n rejectUnauthorized: merged.rejectUnauthorized ?? true,\n minVersion: merged.minVersion,\n });\n\n tlsSocket.once(\"secureConnect\", () => {\n this.socket = tlsSocket;\n this._secure = true;\n resolve();\n });\n tlsSocket.once(\"error\", reject);\n });\n }\n\n /** Writes raw bytes to the socket. */\n async write(data: Uint8Array): Promise<void> {\n if (!this.socket) {\n throw new Error(\"Socket not connected\");\n }\n\n await new Promise<void>((resolve, reject) => {\n this.socket?.write(Buffer.from(data), (err: Error | null | undefined) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n });\n }\n\n /** Reads incoming socket data as an async iterable of byte chunks. */\n async *read(): AsyncGenerator<Uint8Array, void, unknown> {\n if (!this.socket) {\n throw new Error(\"Socket not connected\");\n }\n\n const socket = this.socket;\n const queue: Uint8Array[] = [];\n let resolveNext: ((value: IteratorResult<Uint8Array>) => void) | null = null;\n let done = false;\n let error: Error | null = null;\n\n const onData = (chunk: Buffer): void => {\n const data = new Uint8Array(chunk);\n if (resolveNext) {\n resolveNext({ value: data, done: false });\n resolveNext = null;\n } else {\n queue.push(data);\n }\n };\n\n const onError = (err: Error): void => {\n error = err;\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Uint8Array, done: true });\n resolveNext = null;\n }\n done = true;\n };\n\n const onClose = (): void => {\n done = true;\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Uint8Array, done: true });\n resolveNext = null;\n }\n };\n\n socket.on(\"data\", onData);\n socket.on(\"error\", onError);\n socket.on(\"close\", onClose);\n\n try {\n while (!done || queue.length > 0) {\n if (error) {\n throw error;\n }\n if (queue.length > 0) {\n yield queue.shift() as Uint8Array;\n continue;\n }\n if (done) {\n break;\n }\n const chunk = await new Promise<IteratorResult<Uint8Array>>((resolve) => {\n resolveNext = resolve;\n });\n if (chunk.done) {\n break;\n }\n yield chunk.value;\n }\n } finally {\n socket.off(\"data\", onData);\n socket.off(\"error\", onError);\n socket.off(\"close\", onClose);\n }\n }\n\n /** Closes the socket connection. */\n async close(): Promise<void> {\n if (!this.socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n this.socket?.end(() => resolve());\n });\n this.socket = null;\n this._connected = false;\n }\n\n /** Opens a plain TCP connection to the SMTP server. */\n private connectPlain(host: string, port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = net.connect({ host, port }, () => resolve());\n socket.setTimeout(this.connectionTimeout);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new Error(\"Connection timeout\"));\n });\n socket.once(\"error\", reject);\n this.socket = socket;\n });\n }\n\n /** Opens a direct TLS connection to the SMTP server. */\n private connectTls(host: string, port: number): Promise<void> {\n warnRejectUnauthorizedDisabled(this.tlsOptions);\n return new Promise((resolve, reject) => {\n const socket = tls.connect(\n {\n host,\n port,\n servername: this.tlsOptions.servername ?? host,\n rejectUnauthorized: this.tlsOptions.rejectUnauthorized ?? true,\n minVersion: this.tlsOptions.minVersion,\n },\n () => resolve(),\n );\n socket.setTimeout(this.connectionTimeout);\n socket.once(\"timeout\", () => {\n socket.destroy();\n reject(new Error(\"Connection timeout\"));\n });\n socket.once(\"error\", reject);\n this.socket = socket;\n });\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "81CB75805F76C9AC64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/auth/oauth2.js
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GOOGLE_TOKEN_URL,
|
|
3
|
-
MICROSOFT_TOKEN_URL,
|
|
4
|
-
OAuth2Client
|
|
5
|
-
} from "../chunk-ym3zzv8b.js";
|
|
6
|
-
import"../chunk-794hc3m4.js";
|
|
7
|
-
import"../chunk-v0bahtg2.js";
|
|
8
|
-
export {
|
|
9
|
-
OAuth2Client,
|
|
10
|
-
MICROSOFT_TOKEN_URL,
|
|
11
|
-
GOOGLE_TOKEN_URL
|
|
12
|
-
};
|
|
1
|
+
import{g as a,h as b,i as c}from"../chunk-j5m37k1c.js";import"../chunk-32dj0v9g.js";import"../chunk-rhade81v.js";export{c as OAuth2Client,b as MICROSOFT_TOKEN_URL,a as GOOGLE_TOKEN_URL};
|
|
13
2
|
|
|
14
|
-
//# debugId=
|
|
3
|
+
//# debugId=8B34C4BC825539BC64756E2164756E21
|
package/dist/auth/oauth2.js.map
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
var J=new TextEncoder;function P(k){let f=typeof k==="string"?J.encode(k):k,j="",A=0;while(A<f.length){let m=f[A]??0,q=f[A+1],z=f[A+2];if(q===void 0){j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[m>>2],j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(m&3)<<4],j+="==";break}if(z===void 0){j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[m>>2],j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(m&3)<<4|q>>4],j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q&15)<<2],j+="=";break}j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[m>>2],j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(m&3)<<4|q>>4],j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(q&15)<<2|z>>6],j+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[z&63],A+=3}return R(j)}function S(k){let f=k.replace(/\s/g,""),j=f.length;if(j===0)return new Uint8Array(0);if(j%4!==0)throw Error("Invalid base64 string length");let A=f.endsWith("==")?2:f.endsWith("=")?1:0,m=j*3/4-A,q=new Uint8Array(m),z=0;for(let D=0;D<j;D+=4){let K=G(f[D]??"="),M=G(f[D+1]??"="),N=G(f[D+2]??"="),O=G(f[D+3]??"="),H=K<<18|M<<12|N<<6|O;if(z<m)q[z++]=H>>16&255;if(z<m)q[z++]=H>>8&255;if(z<m)q[z++]=H&255}return q}function W(k){if(!Q(k))return k;return`=?UTF-8?B?${P(k).replace(/\r\n/g,"")}?=`}function Q(k){for(let f=0;f<k.length;f++)if(k.charCodeAt(f)>127)return!0;return!1}function G(k){if(k==="=")return 0;let f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(k);if(f===-1)throw Error(`Invalid base64 character: ${k}`);return f}function R(k){let f=[];for(let j=0;j<k.length;j+=76)f.push(k.slice(j,j+76));return f.join(`\r
|
|
2
|
+
`)}function X(k){return J.encode(k)}
|
|
3
|
+
export{P as G,S as H,W as I,X as J};
|
|
4
|
+
|
|
5
|
+
//# debugId=34038839FA03CAE564756E2164756E21
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"// src/core/base64.ts\n\nconst BASE64_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nconst BASE64_LINE_LENGTH = 76;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * Encode a Uint8Array or string to Base64.\n * Uses TextEncoder + manual base64 to support binary data correctly.\n */\nexport function encodeBase64(data: Uint8Array | string): string {\n const bytes = typeof data === \"string\" ? encoder.encode(data) : data;\n let result = \"\";\n let i = 0;\n\n while (i < bytes.length) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1];\n const b2 = bytes[i + 2];\n\n if (b1 === undefined) {\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[(b0 & 0x03) << 4];\n result += \"==\";\n break;\n }\n\n if (b2 === undefined) {\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)];\n result += BASE64_CHARS[(b1 & 0x0f) << 2];\n result += \"=\";\n break;\n }\n\n result += BASE64_CHARS[b0 >> 2];\n result += BASE64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)];\n result += BASE64_CHARS[((b1 & 0x0f) << 2) | (b2 >> 6)];\n result += BASE64_CHARS[b2 & 0x3f];\n i += 3;\n }\n\n return wrapBase64Lines(result);\n}\n\n/**\n * Decode a Base64 string to Uint8Array.\n */\nexport function decodeBase64(data: string): Uint8Array {\n const cleaned = data.replace(/\\s/g, \"\");\n const len = cleaned.length;\n\n if (len === 0) {\n return new Uint8Array(0);\n }\n\n if (len % 4 !== 0) {\n throw new Error(\"Invalid base64 string length\");\n }\n\n const padding = cleaned.endsWith(\"==\") ? 2 : cleaned.endsWith(\"=\") ? 1 : 0;\n const outputLen = (len * 3) / 4 - padding;\n const output = new Uint8Array(outputLen);\n\n let outIndex = 0;\n for (let i = 0; i < len; i += 4) {\n const c0 = base64CharToValue(cleaned[i] ?? \"=\");\n const c1 = base64CharToValue(cleaned[i + 1] ?? \"=\");\n const c2 = base64CharToValue(cleaned[i + 2] ?? \"=\");\n const c3 = base64CharToValue(cleaned[i + 3] ?? \"=\");\n const triple = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;\n\n if (outIndex < outputLen) {\n output[outIndex++] = (triple >> 16) & 0xff;\n }\n if (outIndex < outputLen) {\n output[outIndex++] = (triple >> 8) & 0xff;\n }\n if (outIndex < outputLen) {\n output[outIndex++] = triple & 0xff;\n }\n }\n\n return output;\n}\n\n/**\n * Encode text using Quoted-Printable (RFC 2045).\n */\nexport function encodeQP(text: string): string {\n const bytes = encoder.encode(text);\n const lines: string[] = [];\n let line = \"\";\n\n for (let i = 0; i < bytes.length; i++) {\n const byte = bytes[i] ?? 0;\n\n if (byte === 0x0a) {\n lines.push(line);\n line = \"\";\n continue;\n }\n\n if (byte === 0x0d) {\n continue;\n }\n\n let encoded: string;\n if (\n (byte >= 33 && byte <= 60) ||\n (byte >= 62 && byte <= 126) ||\n byte === 0x09 ||\n byte === 0x20\n ) {\n encoded = String.fromCharCode(byte);\n } else {\n encoded = `=${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }\n\n if (line.length + encoded.length > 75) {\n lines.push(`${line}=`);\n line = encoded;\n } else {\n line += encoded;\n }\n }\n\n if (line.length > 0) {\n lines.push(line);\n }\n\n return lines.join(\"\\r\\n\");\n}\n\n/**\n * Encode an email header value per RFC 2047.\n * Non-ASCII values become: =?UTF-8?B?<base64>?=\n */\nexport function encodeHeader(value: string): string {\n if (!needsEncoding(value)) {\n return value;\n }\n\n const encoded = encodeBase64(value).replace(/\\r\\n/g, \"\");\n return `=?UTF-8?B?${encoded}?=`;\n}\n\n/**\n * Returns true if the string contains non-ASCII characters\n * and therefore requires RFC 2047 encoding in headers.\n */\nexport function needsEncoding(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 127) {\n return true;\n }\n }\n return false;\n}\n\nfunction base64CharToValue(char: string): number {\n if (char === \"=\") {\n return 0;\n }\n const index = BASE64_CHARS.indexOf(char);\n if (index === -1) {\n throw new Error(`Invalid base64 character: ${char}`);\n }\n return index;\n}\n\nfunction wrapBase64Lines(base64: string): string {\n const lines: string[] = [];\n for (let i = 0; i < base64.length; i += BASE64_LINE_LENGTH) {\n lines.push(base64.slice(i, i + BASE64_LINE_LENGTH));\n }\n return lines.join(\"\\r\\n\");\n}\n\n/** Decode bytes to UTF-8 string. */\nexport function decodeUtf8(bytes: Uint8Array): string {\n return decoder.decode(bytes);\n}\n\n/** Encode string to UTF-8 bytes. */\nexport function encodeUtf8(text: string): Uint8Array {\n return encoder.encode(text);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "AAKA,IAAM,EAAU,IAAI,YAOb,SAAS,CAAY,CAAC,EAAmC,CAC9D,IAAM,EAAQ,OAAO,IAAS,SAAW,EAAQ,OAAO,CAAI,EAAI,EAC5D,EAAS,GACT,EAAI,EAER,MAAO,EAAI,EAAM,OAAQ,CACvB,IAAM,EAAK,EAAM,IAAM,EACjB,EAAK,EAAM,EAAI,GACf,EAAK,EAAM,EAAI,GAErB,GAAI,IAAO,OAAW,CACpB,GArBe,mEAqBQ,GAAM,GAC7B,GAtBe,mEAsBS,GAAK,IAAS,GACtC,GAAU,KACV,MAGF,GAAI,IAAO,OAAW,CACpB,GA5Be,mEA4BQ,GAAM,GAC7B,GA7Be,mEA6BU,GAAK,IAAS,EAAM,GAAM,GACnD,GA9Be,mEA8BS,GAAK,KAAS,GACtC,GAAU,IACV,MAGF,GAnCiB,mEAmCM,GAAM,GAC7B,GApCiB,mEAoCQ,GAAK,IAAS,EAAM,GAAM,GACnD,GArCiB,mEAqCQ,GAAK,KAAS,EAAM,GAAM,GACnD,GAtCiB,mEAsCM,EAAK,IAC5B,GAAK,EAGP,OAAO,EAAgB,CAAM,EAMxB,SAAS,CAAY,CAAC,EAA0B,CACrD,IAAM,EAAU,EAAK,QAAQ,MAAO,EAAE,EAChC,EAAM,EAAQ,OAEpB,GAAI,IAAQ,EACV,OAAO,IAAI,WAAW,CAAC,EAGzB,GAAI,EAAM,IAAM,EACd,MAAU,MAAM,8BAA8B,EAGhD,IAAM,EAAU,EAAQ,SAAS,IAAI,EAAI,EAAI,EAAQ,SAAS,GAAG,EAAI,EAAI,EACnE,EAAa,EAAM,EAAK,EAAI,EAC5B,EAAS,IAAI,WAAW,CAAS,EAEnC,EAAW,EACf,QAAS,EAAI,EAAG,EAAI,EAAK,GAAK,EAAG,CAC/B,IAAM,EAAK,EAAkB,EAAQ,IAAM,GAAG,EACxC,EAAK,EAAkB,EAAQ,EAAI,IAAM,GAAG,EAC5C,EAAK,EAAkB,EAAQ,EAAI,IAAM,GAAG,EAC5C,EAAK,EAAkB,EAAQ,EAAI,IAAM,GAAG,EAC5C,EAAU,GAAM,GAAO,GAAM,GAAO,GAAM,EAAK,EAErD,GAAI,EAAW,EACb,EAAO,KAAe,GAAU,GAAM,IAExC,GAAI,EAAW,EACb,EAAO,KAAe,GAAU,EAAK,IAEvC,GAAI,EAAW,EACb,EAAO,KAAc,EAAS,IAIlC,OAAO,EAuDF,SAAS,CAAY,CAAC,EAAuB,CAClD,GAAI,CAAC,EAAc,CAAK,EACtB,OAAO,EAIT,MAAO,aADS,EAAa,CAAK,EAAE,QAAQ,QAAS,EAAE,MAQlD,SAAS,CAAa,CAAC,EAAuB,CACnD,QAAS,EAAI,EAAG,EAAI,EAAK,OAAQ,IAC/B,GAAI,EAAK,WAAW,CAAC,EAAI,IACvB,MAAO,GAGX,MAAO,GAGT,SAAS,CAAiB,CAAC,EAAsB,CAC/C,GAAI,IAAS,IACX,MAAO,GAET,IAAM,EApKa,mEAoKQ,QAAQ,CAAI,EACvC,GAAI,IAAU,GACZ,MAAU,MAAM,6BAA6B,GAAM,EAErD,OAAO,EAGT,SAAS,CAAe,CAAC,EAAwB,CAC/C,IAAM,EAAkB,CAAC,EACzB,QAAS,EAAI,EAAG,EAAI,EAAO,OAAQ,GA5KV,GA6KvB,EAAM,KAAK,EAAO,MAAM,EAAG,EA7KJ,EA6K0B,CAAC,EAEpD,OAAO,EAAM,KAAK;AAAA,CAAM,EASnB,SAAS,CAAU,CAAC,EAA0B,CACnD,OAAO,EAAQ,OAAO,CAAI",
|
|
8
|
+
"debugId": "34038839FA03CAE564756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{K as C}from"./chunk-rhade81v.js";async function K(D,y){let E=D??[],j=[];for(let b of E){if(b.content instanceof Uint8Array){j.push(b);continue}if(b.path){let z;try{z=await import("node:fs/promises")}catch{throw Error("attachment.path is not supported on this runtime — use attachment.content (Uint8Array) instead")}if(y?.basePath){let{resolve:A,sep:H}=await import("node:path"),k=A(b.path),w=A(y.basePath);if(!(k===w||k.startsWith(w+H)))throw Error(`[sently] Attachment path "${k}" escapes basePath "${w}". Use absolute paths within the allowed directory.`)}let F=await z.readFile(b.path),{path:I,...G}=b;j.push({...G,content:new Uint8Array(F)});continue}if(typeof b.content==="string"){j.push(b);continue}j.push(b)}return j}
|
|
2
|
+
export{K as l};
|
|
3
|
+
|
|
4
|
+
//# debugId=FC32A4B8D9F426A964756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/transports/resolve-attachments.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { Attachment } from \"../core/types.js\";\n\n/** Options for {@link resolveAttachments}. */\nexport interface ResolveAttachmentsOptions {\n /**\n * If set, attachment paths must resolve within this directory.\n * Prevents path traversal via `..` segments and sibling-directory\n * prefix matches. Opt-in only.\n *\n * Note: this check uses `node:path` `resolve()`, which does NOT\n * dereference symlinks. A symlink located inside `basePath` that\n * points outside of it will pass this check. If symlink traversal is\n * a concern, resolve paths with `fs.realpath()` before passing them in.\n */\n basePath?: string;\n}\n\n/**\n * Resolve attachment.path to in-memory Uint8Array content.\n * @throws When attachment.path is used on runtimes without node:fs/promises\n */\nexport async function resolveAttachments(\n attachments: Attachment[] | undefined,\n options?: ResolveAttachmentsOptions,\n): Promise<Attachment[]> {\n const list = attachments ?? [];\n const resolved: Attachment[] = [];\n\n for (const attachment of list) {\n if (attachment.content instanceof Uint8Array) {\n resolved.push(attachment);\n continue;\n }\n\n if (attachment.path) {\n let fs: typeof import(\"node:fs/promises\");\n try {\n fs = await import(\"node:fs/promises\");\n } catch {\n throw new Error(\n \"attachment.path is not supported on this runtime — use attachment.content (Uint8Array) instead\",\n );\n }\n\n if (options?.basePath) {\n const { resolve, sep } = await import(\"node:path\");\n const resolvedPath = resolve(attachment.path);\n const resolvedBase = resolve(options.basePath);\n // startsWith alone is vulnerable: \"/var/data-secret\" passes \"/var/data\".\n // Require an exact match or a trailing path separator.\n const isWithin =\n resolvedPath === resolvedBase || resolvedPath.startsWith(resolvedBase + sep);\n if (!isWithin) {\n throw new Error(\n `[sently] Attachment path \"${resolvedPath}\" escapes basePath \"${resolvedBase}\". ` +\n \"Use absolute paths within the allowed directory.\",\n );\n }\n }\n\n const data = await fs.readFile(attachment.path);\n const { path: _path, ...rest } = attachment;\n resolved.push({ ...rest, content: new Uint8Array(data) });\n continue;\n }\n\n if (typeof attachment.content === \"string\") {\n resolved.push(attachment);\n continue;\n }\n\n resolved.push(attachment);\n }\n\n return resolved;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "wCAqBA,eAAsB,CAAkB,CACtC,EACA,EACuB,CACvB,IAAM,EAAO,GAAe,CAAC,EACvB,EAAyB,CAAC,EAEhC,QAAW,KAAc,EAAM,CAC7B,GAAI,EAAW,mBAAmB,WAAY,CAC5C,EAAS,KAAK,CAAU,EACxB,SAGF,GAAI,EAAW,KAAM,CACnB,IAAI,EACJ,GAAI,CACF,EAAK,KAAa,4BAClB,KAAM,CACN,MAAU,MACR,gGACF,EAGF,GAAI,GAAS,SAAU,CACrB,IAAQ,UAAS,OAAQ,KAAa,qBAChC,EAAe,EAAQ,EAAW,IAAI,EACtC,EAAe,EAAQ,EAAQ,QAAQ,EAK7C,GAAI,EADF,IAAiB,GAAgB,EAAa,WAAW,EAAe,CAAG,GAE3E,MAAU,MACR,6BAA6B,wBAAmC,sDAElE,EAIJ,IAAM,EAAO,MAAM,EAAG,SAAS,EAAW,IAAI,GACtC,KAAM,KAAU,GAAS,EACjC,EAAS,KAAK,IAAK,EAAM,QAAS,IAAI,WAAW,CAAI,CAAE,CAAC,EACxD,SAGF,GAAI,OAAO,EAAW,UAAY,SAAU,CAC1C,EAAS,KAAK,CAAU,EACxB,SAGF,EAAS,KAAK,CAAU,EAG1B,OAAO",
|
|
8
|
+
"debugId": "FC32A4B8D9F426A964756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|