@samva/email-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -0
- package/dist/index.d.mts +28 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +251 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# `@samva/email-sdk`
|
|
2
|
+
|
|
3
|
+
Provider-owned Samva community adapter for
|
|
4
|
+
[Email SDK](https://email-sdk.dev). It targets `@opencoredev/email-sdk@^1.1.0`
|
|
5
|
+
and `samva@^0.2.0`.
|
|
6
|
+
|
|
7
|
+
This adapter is maintained by Samva as a community integration; it is not an
|
|
8
|
+
official built-in Email SDK adapter. Its current evidence is deterministic,
|
|
9
|
+
no-network contract tests and packed-consumer validation. A live Samva account
|
|
10
|
+
send has not yet been performed for this release.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
bun add @samva/email-sdk @opencoredev/email-sdk samva
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Register the adapter
|
|
19
|
+
|
|
20
|
+
Keep the Samva API key on the server. The package does not read environment
|
|
21
|
+
variables or load `.env` files.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createEmailClient } from "@opencoredev/email-sdk";
|
|
25
|
+
import { samva } from "@samva/email-sdk";
|
|
26
|
+
|
|
27
|
+
const email = createEmailClient({
|
|
28
|
+
adapters: [samva({ apiKey: process.env.SAMVA_API_KEY! })],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const result = await email.send({
|
|
32
|
+
from: "Samva <hello@example.com>",
|
|
33
|
+
to: "ada@example.com",
|
|
34
|
+
subject: "Welcome",
|
|
35
|
+
html: "<p>Your workspace is ready.</p>",
|
|
36
|
+
text: "Your workspace is ready.",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(result.adapter, result.id);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
You can also register the adapter through a plugin:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { createEmailClient } from "@opencoredev/email-sdk";
|
|
46
|
+
import { samvaPlugin } from "@samva/email-sdk";
|
|
47
|
+
|
|
48
|
+
const email = createEmailClient({
|
|
49
|
+
plugins: [samvaPlugin({ apiKey: process.env.SAMVA_API_KEY! })],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`samva()` also accepts `baseUrl`, `fetch`, and `headers`, or a preconfigured
|
|
54
|
+
Samva Promise `client`. An API key is required unless a client is injected.
|
|
55
|
+
|
|
56
|
+
## Field support
|
|
57
|
+
|
|
58
|
+
| Email SDK input | Behavior |
|
|
59
|
+
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
+
| `from`, `to`, `cc`, `bcc` | Bare, display-form, and object addresses become Samva address objects; malformed or ambiguous forms are rejected. |
|
|
61
|
+
| `replyTo` | Bare addresses are forwarded. Display names are rejected because Samva cannot preserve them. |
|
|
62
|
+
| `subject`, `html`, `text` | Forwarded without rewriting. |
|
|
63
|
+
| `metadata` | Forwarded to Samva message metadata. |
|
|
64
|
+
| In-memory attachments | Raw or base64 strings, `Uint8Array`, `ArrayBuffer`, and `Blob` become base64 with exact byte size. `contentType` is required. |
|
|
65
|
+
| `headers`, `tags`, `sendAt` | Rejected before the Samva client is called. |
|
|
66
|
+
| Attachment `path`, `contentId`, `disposition` | Rejected before the Samva client is called. URL attachment paths are not supported. |
|
|
67
|
+
| Send `idempotencyKey` | Rejected before the Samva client is called. |
|
|
68
|
+
|
|
69
|
+
Capabilities are declared exactly as follows:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
{
|
|
73
|
+
repeatedHeaders: false,
|
|
74
|
+
idempotency: "none",
|
|
75
|
+
scheduling: false,
|
|
76
|
+
personalized: "expanded",
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Email SDK expands personalized sends into one Samva call per recipient. The
|
|
81
|
+
adapter intentionally has no native `sendPersonalized` method.
|
|
82
|
+
|
|
83
|
+
## Results and failures
|
|
84
|
+
|
|
85
|
+
Successful sends return `{ adapter: "samva", id, raw }`. `raw` is the Samva
|
|
86
|
+
message result. Recipient queue states are not converted into final accepted or
|
|
87
|
+
rejected delivery claims.
|
|
88
|
+
|
|
89
|
+
Unsupported or lossy input throws `EmailValidationError` without calling
|
|
90
|
+
Samva. API and transport failures become a redacted `EmailAdapterError` with
|
|
91
|
+
status and `x-request-id` when available. Retryability is enabled for statuses
|
|
92
|
+
408, 409, 425, 429, and 5xx. Ambiguous transport, server, and malformed-success
|
|
93
|
+
outcomes use `delivery: "unknown"`; fallback therefore stops unless the
|
|
94
|
+
application explicitly opts into continuing after unknown delivery.
|
|
95
|
+
|
|
96
|
+
Abort signals are forwarded to the Samva Promise client and retain Email SDK's
|
|
97
|
+
`EmailAbortError` behavior. The adapter send path uses Web APIs and has no
|
|
98
|
+
`node:*` imports.
|
|
99
|
+
|
|
100
|
+
See Email SDK's
|
|
101
|
+
[fallback and retry model](https://email-sdk.dev/docs/concepts/fallbacks-and-retries)
|
|
102
|
+
before composing multiple adapters.
|
|
103
|
+
|
|
104
|
+
## More examples
|
|
105
|
+
|
|
106
|
+
- [Samva + Email SDK cookbook](../../cookbooks/email-sdk.md)
|
|
107
|
+
- [React Email rendering cookbook](../../cookbooks/react-email.md)
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
[MIT](../../LICENSE)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { EmailAdapter, EmailPlugin } from "@opencoredev/email-sdk";
|
|
2
|
+
import { SamvaClient } from "samva";
|
|
3
|
+
|
|
4
|
+
//#region src/samva.d.ts
|
|
5
|
+
/** Factory options for the provider-owned Samva community adapter. */
|
|
6
|
+
type SamvaAdapterOptions = {
|
|
7
|
+
/** Samva API key. Required unless `client` is injected. */
|
|
8
|
+
readonly apiKey?: string;
|
|
9
|
+
/** Override the Samva API origin. */
|
|
10
|
+
readonly baseUrl?: string;
|
|
11
|
+
/** Fetch implementation used by the Samva client. */
|
|
12
|
+
readonly fetch?: typeof fetch;
|
|
13
|
+
/** Headers added to every Samva request. */
|
|
14
|
+
readonly headers?: HeadersInit;
|
|
15
|
+
/** Preconfigured client, primarily for dependency injection and tests. */
|
|
16
|
+
readonly client?: SamvaClient;
|
|
17
|
+
};
|
|
18
|
+
/** Raw Samva Promise-client result returned from `email.send`. */
|
|
19
|
+
type SamvaSendResult = Awaited<ReturnType<SamvaClient["email"]["send"]>>;
|
|
20
|
+
/** The literal Email SDK adapter type returned by {@link samva}. */
|
|
21
|
+
type SamvaEmailAdapter = EmailAdapter<"samva", SamvaClient, SamvaSendResult>;
|
|
22
|
+
/** Create an Email SDK adapter backed by the Samva Promise client. */
|
|
23
|
+
declare function samva(options: SamvaAdapterOptions): SamvaEmailAdapter;
|
|
24
|
+
/** Register a single Samva adapter through Email SDK's plugin interface. */
|
|
25
|
+
declare function samvaPlugin(options: SamvaAdapterOptions): EmailPlugin;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { type SamvaAdapterOptions, type SamvaEmailAdapter, type SamvaSendResult, samva, samvaPlugin };
|
|
28
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/samva.ts"],"sourcesContent":[],"mappings":";;;;;KASY,mBAAA;EAAA;EAMc,SAAA,MAAA,CAAA,EAAA,MAAA;EAEL;EAED,SAAA,OAAA,CAAA,EAAA,MAAA;EAAW;EAInB,SAAA,KAAA,CAAA,EAAA,OARc,KAQC;EAAsB;EAAX,SAAA,OAAA,CAAA,EANjB,WAMiB;EAAR;EAAO,SAAA,MAAA,CAAA,EAJjB,WAIiB;AAGrC,CAAA;;AAAmE,KAHvD,eAAA,GAAkB,OAGqC,CAH7B,UAG6B,CAHlB,WAGkB,CAAA,OAAA,CAAA,CAAA,MAAA,CAAA,CAAA,CAAA;;AAAvB,KAAhC,iBAAA,GAAoB,YAAY,CAAA,OAAA,EAAU,WAAV,EAAuB,eAAvB,CAAA;AA8B5C;AA0CgB,iBA1CA,KAAA,CA0CqB,OAAA,EA1CN,mBA0C4B,CAAA,EA1CN,iBA0CiB;;iBAAtD,WAAA,UAAqB,sBAAsB"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { EmailAdapterError, EmailValidationError } from "@opencoredev/email-sdk";
|
|
2
|
+
import { createClient } from "samva";
|
|
3
|
+
|
|
4
|
+
//#region src/failures.ts
|
|
5
|
+
const knownNotSentTags = new Set([
|
|
6
|
+
"ConflictError",
|
|
7
|
+
"EmailChannelError",
|
|
8
|
+
"FlagDisabledError",
|
|
9
|
+
"ForbiddenError",
|
|
10
|
+
"PaymentRequiredError",
|
|
11
|
+
"RateLimitedError",
|
|
12
|
+
"ResourceNotFoundError",
|
|
13
|
+
"UnauthorizedError",
|
|
14
|
+
"ValidationError"
|
|
15
|
+
]);
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === "object" && value !== null;
|
|
18
|
+
}
|
|
19
|
+
function numericField(error, key) {
|
|
20
|
+
const value = error[key];
|
|
21
|
+
return typeof value === "number" && Number.isInteger(value) ? value : void 0;
|
|
22
|
+
}
|
|
23
|
+
function statusOf(error) {
|
|
24
|
+
return numericField(error, "statusCode") ?? numericField(error, "status");
|
|
25
|
+
}
|
|
26
|
+
function requestIdOf(error) {
|
|
27
|
+
const direct = error.requestId;
|
|
28
|
+
if (typeof direct === "string" && direct.length > 0) return direct;
|
|
29
|
+
const response = error.response;
|
|
30
|
+
if (!isRecord(response)) return void 0;
|
|
31
|
+
const headers = response.headers;
|
|
32
|
+
if (!isRecord(headers) || typeof headers.get !== "function") return void 0;
|
|
33
|
+
const value = headers.get("x-request-id");
|
|
34
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
35
|
+
}
|
|
36
|
+
function isRetryableStatus(status) {
|
|
37
|
+
return status === 408 || status === 409 || status === 425 || status === 429 || status !== void 0 && status >= 500;
|
|
38
|
+
}
|
|
39
|
+
function deliveryOf(error) {
|
|
40
|
+
const tag = error["_tag"];
|
|
41
|
+
if (tag === "SamvaTransportError") return "unknown";
|
|
42
|
+
if (tag === "EmailChannelError" && error.reason === "transport") return "unknown";
|
|
43
|
+
return typeof tag === "string" && knownNotSentTags.has(tag) ? "not_sent" : "unknown";
|
|
44
|
+
}
|
|
45
|
+
/** Preserve an abort so Email SDK core can apply its own abort semantics. */
|
|
46
|
+
function isAbortFailure(error, signal) {
|
|
47
|
+
return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
|
|
48
|
+
}
|
|
49
|
+
/** Convert Samva API and transport failures without retaining provider bodies or credentials. */
|
|
50
|
+
function toEmailAdapterError(error) {
|
|
51
|
+
const record = isRecord(error) ? error : {};
|
|
52
|
+
const status = statusOf(record);
|
|
53
|
+
const requestId = requestIdOf(record);
|
|
54
|
+
return new EmailAdapterError("Samva email send failed.", {
|
|
55
|
+
adapter: "samva",
|
|
56
|
+
...status === void 0 ? {} : { status },
|
|
57
|
+
...requestId === void 0 ? {} : { requestId },
|
|
58
|
+
retryable: isRetryableStatus(status),
|
|
59
|
+
delivery: deliveryOf(record)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/message.ts
|
|
65
|
+
const ordinaryEmail = /^[^\s@<>,]+@[^\s@<>,]+$/;
|
|
66
|
+
function validationError(field, detail) {
|
|
67
|
+
return new EmailValidationError(`Samva cannot send email with ${field}: ${detail}.`);
|
|
68
|
+
}
|
|
69
|
+
function normalizeName(name, field) {
|
|
70
|
+
if (name === void 0) return void 0;
|
|
71
|
+
const normalized = name.trim();
|
|
72
|
+
if (normalized.length === 0 || /[<>\r\n]/.test(normalized)) throw validationError(field, "the display name is malformed");
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
function assertEmail(email, field) {
|
|
76
|
+
const normalized = email.trim();
|
|
77
|
+
if (normalized !== email || !ordinaryEmail.test(normalized)) throw validationError(field, `the address "${email}" is malformed or ambiguous`);
|
|
78
|
+
return normalized;
|
|
79
|
+
}
|
|
80
|
+
function normalizeStringAddress(address, field) {
|
|
81
|
+
const normalized = address.trim();
|
|
82
|
+
if (normalized !== address || normalized.length === 0) throw validationError(field, "the address is empty or has surrounding whitespace");
|
|
83
|
+
if (!(normalized.includes("<") || normalized.includes(">"))) return { email: assertEmail(normalized, field) };
|
|
84
|
+
const display = normalized.match(/^(.+?)\s*<([^<>]+)>$/);
|
|
85
|
+
if (!display) throw validationError(field, `the address "${address}" is malformed or ambiguous`);
|
|
86
|
+
const name = normalizeName(display[1], field);
|
|
87
|
+
return {
|
|
88
|
+
email: assertEmail(display[2], field),
|
|
89
|
+
...name === void 0 ? {} : { name }
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function normalizeAddress(address, field) {
|
|
93
|
+
if (typeof address === "string") return normalizeStringAddress(address, field);
|
|
94
|
+
if (!address || typeof address !== "object") throw validationError(field, "the address is malformed");
|
|
95
|
+
const name = normalizeName(address.name, field);
|
|
96
|
+
return {
|
|
97
|
+
email: assertEmail(address.email, field),
|
|
98
|
+
...name === void 0 ? {} : { name }
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function normalizeAddresses(addresses, field) {
|
|
102
|
+
if (addresses === void 0) return void 0;
|
|
103
|
+
const values = Array.isArray(addresses) ? addresses : [addresses];
|
|
104
|
+
if (values.length === 0) throw validationError(field, "at least one address is required");
|
|
105
|
+
return values.map((address) => normalizeAddress(address, field));
|
|
106
|
+
}
|
|
107
|
+
function normalizeReplyTo(addresses) {
|
|
108
|
+
const normalized = normalizeAddresses(addresses, "replyTo");
|
|
109
|
+
if (!normalized) return void 0;
|
|
110
|
+
for (const address of normalized) if (address.name !== void 0) throw validationError("replyTo", "Samva cannot preserve reply-to display names");
|
|
111
|
+
return normalized.map(({ email }) => email);
|
|
112
|
+
}
|
|
113
|
+
function bytesToBase64(bytes) {
|
|
114
|
+
let binary = "";
|
|
115
|
+
for (let offset = 0; offset < bytes.length; offset += 32768) binary += String.fromCharCode(...bytes.subarray(offset, offset + 32768));
|
|
116
|
+
return btoa(binary);
|
|
117
|
+
}
|
|
118
|
+
function decodeBase64(content, filename) {
|
|
119
|
+
if (content.length % 4 === 1 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(content)) throw validationError(`attachment "${filename}"`, "base64 content is malformed");
|
|
120
|
+
const decoded = atob(content);
|
|
121
|
+
return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
|
|
122
|
+
}
|
|
123
|
+
function attachmentBytes(attachment) {
|
|
124
|
+
const content = attachment.content;
|
|
125
|
+
if (typeof content === "string") return attachment.contentEncoding === "base64" ? decodeBase64(content, attachment.filename) : new TextEncoder().encode(content);
|
|
126
|
+
if (content instanceof Uint8Array) return content;
|
|
127
|
+
if (content instanceof ArrayBuffer) return new Uint8Array(content);
|
|
128
|
+
if (content instanceof Blob) return content.arrayBuffer().then((buffer) => new Uint8Array(buffer));
|
|
129
|
+
throw validationError(`attachment "${attachment.filename}"`, "content is malformed");
|
|
130
|
+
}
|
|
131
|
+
function validateAttachment(attachment) {
|
|
132
|
+
const field = `attachment "${attachment.filename}"`;
|
|
133
|
+
if (!attachment.filename || attachment.filename.trim() !== attachment.filename) throw validationError(field, "filename is empty or has surrounding whitespace");
|
|
134
|
+
if (attachment.path !== void 0) throw validationError(field, "path and URL attachments are not supported");
|
|
135
|
+
if (attachment.contentId !== void 0) throw validationError(field, "contentId is not supported");
|
|
136
|
+
if (attachment.disposition !== void 0) throw validationError(field, "disposition is not supported");
|
|
137
|
+
if (attachment.contentType === void 0 || attachment.contentType.length === 0 || attachment.contentType.trim() !== attachment.contentType) throw validationError(field, "contentType is required without surrounding whitespace");
|
|
138
|
+
if (attachment.content === void 0) throw validationError(field, "in-memory content is required");
|
|
139
|
+
return attachment.contentType;
|
|
140
|
+
}
|
|
141
|
+
async function normalizeAttachment(attachment) {
|
|
142
|
+
const contentType = validateAttachment(attachment);
|
|
143
|
+
const bytes = await attachmentBytes(attachment);
|
|
144
|
+
return {
|
|
145
|
+
filename: attachment.filename,
|
|
146
|
+
content: bytesToBase64(bytes),
|
|
147
|
+
contentType,
|
|
148
|
+
size: bytes.byteLength
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** Validate every normalized Email SDK field that the Samva adapter accepts. */
|
|
152
|
+
function validateMessage(message) {
|
|
153
|
+
normalizeAddress(message.from, "from");
|
|
154
|
+
normalizeAddresses(message.to, "to");
|
|
155
|
+
normalizeAddresses(message.cc, "cc");
|
|
156
|
+
normalizeAddresses(message.bcc, "bcc");
|
|
157
|
+
normalizeReplyTo(message.replyTo);
|
|
158
|
+
if ((message.headers?.length ?? 0) > 0) throw validationError("headers", "custom headers are not supported");
|
|
159
|
+
if ((message.tags?.length ?? 0) > 0) throw validationError("tags", "tags are not supported");
|
|
160
|
+
if (message.sendAt !== void 0) throw validationError("sendAt", "scheduling is not supported");
|
|
161
|
+
for (const attachment of message.attachments ?? []) validateAttachment(attachment);
|
|
162
|
+
}
|
|
163
|
+
/** Map a validated Email SDK message to the Samva Promise client's email input. */
|
|
164
|
+
async function toSamvaMessage(message) {
|
|
165
|
+
validateMessage(message);
|
|
166
|
+
const from = normalizeAddress(message.from, "from");
|
|
167
|
+
const to = normalizeAddresses(message.to, "to");
|
|
168
|
+
const cc = normalizeAddresses(message.cc, "cc");
|
|
169
|
+
const bcc = normalizeAddresses(message.bcc, "bcc");
|
|
170
|
+
const replyTo = normalizeReplyTo(message.replyTo);
|
|
171
|
+
const attachments = await Promise.all((message.attachments ?? []).map(normalizeAttachment));
|
|
172
|
+
return {
|
|
173
|
+
from,
|
|
174
|
+
to,
|
|
175
|
+
...cc === void 0 ? {} : { cc },
|
|
176
|
+
...bcc === void 0 ? {} : { bcc },
|
|
177
|
+
...replyTo === void 0 ? {} : { replyTo },
|
|
178
|
+
subject: message.subject,
|
|
179
|
+
...message.html === void 0 ? {} : { html: message.html },
|
|
180
|
+
...message.text === void 0 ? {} : { text: message.text },
|
|
181
|
+
...message.metadata === void 0 ? {} : { metadata: message.metadata },
|
|
182
|
+
...attachments.length === 0 ? {} : { attachments }
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/samva.ts
|
|
188
|
+
const capabilities = {
|
|
189
|
+
repeatedHeaders: false,
|
|
190
|
+
idempotency: "none",
|
|
191
|
+
scheduling: false,
|
|
192
|
+
personalized: "expanded"
|
|
193
|
+
};
|
|
194
|
+
function configuredClient(options) {
|
|
195
|
+
if (options.client) return options.client;
|
|
196
|
+
if (!options.apiKey) throw new EmailValidationError("The Samva Email SDK adapter requires an apiKey unless a client is injected.");
|
|
197
|
+
return createClient({
|
|
198
|
+
apiKey: options.apiKey,
|
|
199
|
+
...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl },
|
|
200
|
+
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
201
|
+
...options.headers === void 0 ? {} : { headers: options.headers }
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function abortReason(signal) {
|
|
205
|
+
if (signal?.reason instanceof Error) return signal.reason;
|
|
206
|
+
return new DOMException("Email sending was aborted.", "AbortError");
|
|
207
|
+
}
|
|
208
|
+
/** Create an Email SDK adapter backed by the Samva Promise client. */
|
|
209
|
+
function samva(options) {
|
|
210
|
+
const client = configuredClient(options);
|
|
211
|
+
return {
|
|
212
|
+
name: "samva",
|
|
213
|
+
capabilities,
|
|
214
|
+
raw: client,
|
|
215
|
+
validate(message) {
|
|
216
|
+
validateMessage(message);
|
|
217
|
+
},
|
|
218
|
+
async send(message, context) {
|
|
219
|
+
if (context.idempotencyKey !== void 0) throw new EmailValidationError("The Samva Email SDK adapter does not support idempotency keys.");
|
|
220
|
+
if (context.signal?.aborted) throw abortReason(context.signal);
|
|
221
|
+
const payload = await toSamvaMessage(message);
|
|
222
|
+
let result;
|
|
223
|
+
try {
|
|
224
|
+
result = await client.email.send(payload, { ...context.signal === void 0 ? {} : { signal: context.signal } });
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (isAbortFailure(error, context.signal)) throw abortReason(context.signal);
|
|
227
|
+
throw toEmailAdapterError(error);
|
|
228
|
+
}
|
|
229
|
+
if (!result || typeof result !== "object" || typeof result.id !== "string") throw new EmailAdapterError("Samva returned a malformed email send response.", {
|
|
230
|
+
adapter: "samva",
|
|
231
|
+
delivery: "unknown"
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
adapter: "samva",
|
|
235
|
+
id: result.id,
|
|
236
|
+
raw: result
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** Register a single Samva adapter through Email SDK's plugin interface. */
|
|
242
|
+
function samvaPlugin(options) {
|
|
243
|
+
return {
|
|
244
|
+
id: "samva",
|
|
245
|
+
adapters: [samva(options)]
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
//#endregion
|
|
250
|
+
export { samva, samvaPlugin };
|
|
251
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["result: SamvaSendResult"],"sources":["../src/failures.ts","../src/message.ts","../src/samva.ts"],"sourcesContent":["import { EmailAdapterError } from \"@opencoredev/email-sdk\";\n\ntype ErrorRecord = Record<string, unknown>;\n\nconst knownNotSentTags = new Set([\n \"ConflictError\",\n \"EmailChannelError\",\n \"FlagDisabledError\",\n \"ForbiddenError\",\n \"PaymentRequiredError\",\n \"RateLimitedError\",\n \"ResourceNotFoundError\",\n \"UnauthorizedError\",\n \"ValidationError\",\n]);\n\nfunction isRecord(value: unknown): value is ErrorRecord {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction numericField(error: ErrorRecord, key: string): number | undefined {\n const value = error[key];\n return typeof value === \"number\" && Number.isInteger(value) ? value : undefined;\n}\n\nfunction statusOf(error: ErrorRecord): number | undefined {\n return numericField(error, \"statusCode\") ?? numericField(error, \"status\");\n}\n\nfunction requestIdOf(error: ErrorRecord): string | undefined {\n const direct = error.requestId;\n if (typeof direct === \"string\" && direct.length > 0) return direct;\n const response = error.response;\n if (!isRecord(response)) return undefined;\n const headers = response.headers;\n if (!isRecord(headers) || typeof headers.get !== \"function\") return undefined;\n const value = headers.get(\"x-request-id\");\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction isRetryableStatus(status: number | undefined): boolean {\n return (\n status === 408 ||\n status === 409 ||\n status === 425 ||\n status === 429 ||\n (status !== undefined && status >= 500)\n );\n}\n\nfunction deliveryOf(error: ErrorRecord): \"not_sent\" | \"unknown\" {\n const tag = error[\"_tag\"];\n if (tag === \"SamvaTransportError\") return \"unknown\";\n if (tag === \"EmailChannelError\" && error.reason === \"transport\") return \"unknown\";\n return typeof tag === \"string\" && knownNotSentTags.has(tag) ? \"not_sent\" : \"unknown\";\n}\n\n/** Preserve an abort so Email SDK core can apply its own abort semantics. */\nexport function isAbortFailure(error: unknown, signal: AbortSignal | undefined): boolean {\n return signal?.aborted === true || (error instanceof Error && error.name === \"AbortError\");\n}\n\n/** Convert Samva API and transport failures without retaining provider bodies or credentials. */\nexport function toEmailAdapterError(error: unknown): EmailAdapterError {\n const record = isRecord(error) ? error : {};\n const status = statusOf(record);\n const requestId = requestIdOf(record);\n return new EmailAdapterError(\"Samva email send failed.\", {\n adapter: \"samva\",\n ...(status === undefined ? {} : { status }),\n ...(requestId === undefined ? {} : { requestId }),\n retryable: isRetryableStatus(status),\n delivery: deliveryOf(record),\n });\n}\n","import { EmailValidationError } from \"@opencoredev/email-sdk\";\nimport type { EmailAddress, EmailAttachment, EmailMessage } from \"@opencoredev/email-sdk\";\nimport type { SamvaClient } from \"samva\";\n\ntype SamvaSendInput = Parameters<SamvaClient[\"email\"][\"send\"]>[0];\ntype SamvaSender = NonNullable<SamvaSendInput[\"from\"]>;\n\ntype NormalizedAddress = {\n readonly email: string;\n readonly name?: string;\n};\n\nconst ordinaryEmail = /^[^\\s@<>,]+@[^\\s@<>,]+$/;\n\nfunction validationError(field: string, detail: string): EmailValidationError {\n return new EmailValidationError(`Samva cannot send email with ${field}: ${detail}.`);\n}\n\nfunction normalizeName(name: string | undefined, field: string): string | undefined {\n if (name === undefined) return undefined;\n const normalized = name.trim();\n if (normalized.length === 0 || /[<>\\r\\n]/.test(normalized)) {\n throw validationError(field, \"the display name is malformed\");\n }\n return normalized;\n}\n\nfunction assertEmail(email: string, field: string): string {\n const normalized = email.trim();\n if (normalized !== email || !ordinaryEmail.test(normalized)) {\n throw validationError(field, `the address \"${email}\" is malformed or ambiguous`);\n }\n return normalized;\n}\n\nfunction normalizeStringAddress(address: string, field: string): NormalizedAddress {\n const normalized = address.trim();\n if (normalized !== address || normalized.length === 0) {\n throw validationError(field, \"the address is empty or has surrounding whitespace\");\n }\n\n const hasAngleBracket = normalized.includes(\"<\") || normalized.includes(\">\");\n if (!hasAngleBracket) return { email: assertEmail(normalized, field) };\n\n const display = normalized.match(/^(.+?)\\s*<([^<>]+)>$/);\n if (!display) {\n throw validationError(field, `the address \"${address}\" is malformed or ambiguous`);\n }\n\n const name = normalizeName(display[1]!, field);\n return {\n email: assertEmail(display[2]!, field),\n ...(name === undefined ? {} : { name }),\n };\n}\n\nfunction normalizeAddress(address: EmailAddress, field: string): NormalizedAddress {\n if (typeof address === \"string\") return normalizeStringAddress(address, field);\n if (!address || typeof address !== \"object\") {\n throw validationError(field, \"the address is malformed\");\n }\n const name = normalizeName(address.name, field);\n return { email: assertEmail(address.email, field), ...(name === undefined ? {} : { name }) };\n}\n\nfunction normalizeAddresses(\n addresses: EmailAddress | readonly EmailAddress[] | undefined,\n field: string,\n): NormalizedAddress[] | undefined {\n if (addresses === undefined) return undefined;\n const values = Array.isArray(addresses) ? addresses : [addresses as EmailAddress];\n if (values.length === 0) throw validationError(field, \"at least one address is required\");\n return values.map((address) => normalizeAddress(address, field));\n}\n\nfunction normalizeReplyTo(addresses: EmailMessage[\"replyTo\"]): readonly string[] | undefined {\n const normalized = normalizeAddresses(addresses, \"replyTo\");\n if (!normalized) return undefined;\n for (const address of normalized) {\n if (address.name !== undefined) {\n throw validationError(\"replyTo\", \"Samva cannot preserve reply-to display names\");\n }\n }\n return normalized.map(({ email }) => email);\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (let offset = 0; offset < bytes.length; offset += 32_768) {\n binary += String.fromCharCode(...bytes.subarray(offset, offset + 32_768));\n }\n return btoa(binary);\n}\n\nfunction decodeBase64(content: string, filename: string): Uint8Array {\n if (\n content.length % 4 === 1 ||\n !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(content)\n ) {\n throw validationError(`attachment \"${filename}\"`, \"base64 content is malformed\");\n }\n const decoded = atob(content);\n return Uint8Array.from(decoded, (character) => character.charCodeAt(0));\n}\n\nfunction attachmentBytes(attachment: EmailAttachment): Promise<Uint8Array> | Uint8Array {\n const content = attachment.content;\n if (typeof content === \"string\") {\n return attachment.contentEncoding === \"base64\"\n ? decodeBase64(content, attachment.filename)\n : new TextEncoder().encode(content);\n }\n if (content instanceof Uint8Array) return content;\n if (content instanceof ArrayBuffer) return new Uint8Array(content);\n if (content instanceof Blob) {\n return content.arrayBuffer().then((buffer) => new Uint8Array(buffer));\n }\n throw validationError(`attachment \"${attachment.filename}\"`, \"content is malformed\");\n}\n\nfunction validateAttachment(attachment: EmailAttachment): string {\n const field = `attachment \"${attachment.filename}\"`;\n if (!attachment.filename || attachment.filename.trim() !== attachment.filename) {\n throw validationError(field, \"filename is empty or has surrounding whitespace\");\n }\n if (attachment.path !== undefined) {\n throw validationError(field, \"path and URL attachments are not supported\");\n }\n if (attachment.contentId !== undefined) {\n throw validationError(field, \"contentId is not supported\");\n }\n if (attachment.disposition !== undefined) {\n throw validationError(field, \"disposition is not supported\");\n }\n if (\n attachment.contentType === undefined ||\n attachment.contentType.length === 0 ||\n attachment.contentType.trim() !== attachment.contentType\n ) {\n throw validationError(field, \"contentType is required without surrounding whitespace\");\n }\n if (attachment.content === undefined) {\n throw validationError(field, \"in-memory content is required\");\n }\n return attachment.contentType;\n}\n\nasync function normalizeAttachment(attachment: EmailAttachment) {\n const contentType = validateAttachment(attachment);\n const bytes = await attachmentBytes(attachment);\n return {\n filename: attachment.filename,\n content: bytesToBase64(bytes),\n contentType,\n size: bytes.byteLength,\n };\n}\n\n/** Validate every normalized Email SDK field that the Samva adapter accepts. */\nexport function validateMessage(message: EmailMessage): void {\n normalizeAddress(message.from, \"from\");\n normalizeAddresses(message.to, \"to\");\n normalizeAddresses(message.cc, \"cc\");\n normalizeAddresses(message.bcc, \"bcc\");\n normalizeReplyTo(message.replyTo);\n\n if ((message.headers?.length ?? 0) > 0) {\n throw validationError(\"headers\", \"custom headers are not supported\");\n }\n if ((message.tags?.length ?? 0) > 0) {\n throw validationError(\"tags\", \"tags are not supported\");\n }\n if (message.sendAt !== undefined) {\n throw validationError(\"sendAt\", \"scheduling is not supported\");\n }\n for (const attachment of message.attachments ?? []) validateAttachment(attachment);\n}\n\n/** Map a validated Email SDK message to the Samva Promise client's email input. */\nexport async function toSamvaMessage(message: EmailMessage): Promise<SamvaSendInput> {\n validateMessage(message);\n const from = normalizeAddress(message.from, \"from\");\n const to = normalizeAddresses(message.to, \"to\")!;\n const cc = normalizeAddresses(message.cc, \"cc\");\n const bcc = normalizeAddresses(message.bcc, \"bcc\");\n const replyTo = normalizeReplyTo(message.replyTo);\n const attachments = await Promise.all((message.attachments ?? []).map(normalizeAttachment));\n\n return {\n from: from as SamvaSender,\n to: to as SamvaSendInput[\"to\"],\n ...(cc === undefined ? {} : { cc: cc as NonNullable<SamvaSendInput[\"cc\"]> }),\n ...(bcc === undefined ? {} : { bcc: bcc as NonNullable<SamvaSendInput[\"bcc\"]> }),\n ...(replyTo === undefined ? {} : { replyTo }),\n subject: message.subject,\n ...(message.html === undefined ? {} : { html: message.html }),\n ...(message.text === undefined ? {} : { text: message.text }),\n ...(message.metadata === undefined ? {} : { metadata: message.metadata }),\n ...(attachments.length === 0 ? {} : { attachments }),\n };\n}\n","import { EmailAdapterError, EmailValidationError } from \"@opencoredev/email-sdk\";\nimport type { EmailAdapter, EmailAdapterCapabilities, EmailPlugin } from \"@opencoredev/email-sdk\";\nimport { createClient } from \"samva\";\nimport type { SamvaClient } from \"samva\";\n\nimport { isAbortFailure, toEmailAdapterError } from \"./failures\";\nimport { toSamvaMessage, validateMessage } from \"./message\";\n\n/** Factory options for the provider-owned Samva community adapter. */\nexport type SamvaAdapterOptions = {\n /** Samva API key. Required unless `client` is injected. */\n readonly apiKey?: string;\n /** Override the Samva API origin. */\n readonly baseUrl?: string;\n /** Fetch implementation used by the Samva client. */\n readonly fetch?: typeof fetch;\n /** Headers added to every Samva request. */\n readonly headers?: HeadersInit;\n /** Preconfigured client, primarily for dependency injection and tests. */\n readonly client?: SamvaClient;\n};\n\n/** Raw Samva Promise-client result returned from `email.send`. */\nexport type SamvaSendResult = Awaited<ReturnType<SamvaClient[\"email\"][\"send\"]>>;\n\n/** The literal Email SDK adapter type returned by {@link samva}. */\nexport type SamvaEmailAdapter = EmailAdapter<\"samva\", SamvaClient, SamvaSendResult>;\n\nconst capabilities = {\n repeatedHeaders: false,\n idempotency: \"none\",\n scheduling: false,\n personalized: \"expanded\",\n} as const satisfies EmailAdapterCapabilities;\n\nfunction configuredClient(options: SamvaAdapterOptions): SamvaClient {\n if (options.client) return options.client;\n if (!options.apiKey) {\n throw new EmailValidationError(\n \"The Samva Email SDK adapter requires an apiKey unless a client is injected.\",\n );\n }\n return createClient({\n apiKey: options.apiKey,\n ...(options.baseUrl === undefined ? {} : { baseUrl: options.baseUrl }),\n ...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n ...(options.headers === undefined ? {} : { headers: options.headers }),\n });\n}\n\nfunction abortReason(signal: AbortSignal | undefined): unknown {\n if (signal?.reason instanceof Error) return signal.reason;\n return new DOMException(\"Email sending was aborted.\", \"AbortError\");\n}\n\n/** Create an Email SDK adapter backed by the Samva Promise client. */\nexport function samva(options: SamvaAdapterOptions): SamvaEmailAdapter {\n const client = configuredClient(options);\n\n return {\n name: \"samva\",\n capabilities,\n raw: client,\n validate(message) {\n validateMessage(message);\n },\n async send(message, context) {\n if (context.idempotencyKey !== undefined) {\n throw new EmailValidationError(\n \"The Samva Email SDK adapter does not support idempotency keys.\",\n );\n }\n if (context.signal?.aborted) throw abortReason(context.signal);\n\n const payload = await toSamvaMessage(message);\n let result: SamvaSendResult;\n try {\n result = await client.email.send(payload, {\n ...(context.signal === undefined ? {} : { signal: context.signal }),\n });\n } catch (error) {\n if (isAbortFailure(error, context.signal)) throw abortReason(context.signal);\n throw toEmailAdapterError(error);\n }\n\n if (!result || typeof result !== \"object\" || typeof result.id !== \"string\") {\n throw new EmailAdapterError(\"Samva returned a malformed email send response.\", {\n adapter: \"samva\",\n delivery: \"unknown\",\n });\n }\n\n return { adapter: \"samva\", id: result.id, raw: result };\n },\n };\n}\n\n/** Register a single Samva adapter through Email SDK's plugin interface. */\nexport function samvaPlugin(options: SamvaAdapterOptions): EmailPlugin {\n return {\n id: \"samva\",\n adapters: [samva(options)],\n };\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@samva/email-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Samva community adapter for Email SDK.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"email",
|
|
7
|
+
"email-sdk",
|
|
8
|
+
"samva",
|
|
9
|
+
"transactional-email"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://samva.dev",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/AryaLabsHQ/samva-integrations/issues"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Arya Labs, Inc.",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/AryaLabsHQ/samva-integrations.git",
|
|
20
|
+
"directory": "packages/email-sdk"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"package.json"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.mts",
|
|
32
|
+
"import": "./dist/index.mjs"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsdown src/index.ts --format esm --dts --external samva --external @opencoredev/email-sdk",
|
|
40
|
+
"prepack": "bun run build",
|
|
41
|
+
"dev": "tsdown src/index.ts --format esm --dts --watch --external samva --external @opencoredev/email-sdk",
|
|
42
|
+
"typecheck": "tsgo --noEmit",
|
|
43
|
+
"test": "vitest run --config vitest.config.ts"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@opencoredev/email-sdk": "^1.1.0",
|
|
47
|
+
"samva": "^0.2.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@opencoredev/email-sdk": "^1.1.0",
|
|
51
|
+
"samva": "^0.2.0"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=22"
|
|
55
|
+
}
|
|
56
|
+
}
|