@samva/email-sdk 0.1.1 → 0.2.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/AGENTS.md +21 -0
- package/LICENSE +21 -0
- package/README.md +5 -4
- package/dist/index.d.mts +0 -1
- package/dist/index.mjs +10 -9
- package/package.json +9 -6
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
package/AGENTS.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @samva/email-sdk consumer guide
|
|
2
|
+
|
|
3
|
+
Provider-owned Samva community adapter for
|
|
4
|
+
[Email SDK](https://email-sdk.dev). Register it with `samva({ apiKey })` or
|
|
5
|
+
through Email SDK's plugin interface with `samvaPlugin({ apiKey })`.
|
|
6
|
+
|
|
7
|
+
- Pass a preconfigured Samva Promise `client` for dependency injection; an API
|
|
8
|
+
key is required otherwise.
|
|
9
|
+
- Inputs are normalized and validated before Samva is called. Unsupported or
|
|
10
|
+
lossy fields (custom `headers`, `tags`, `sendAt`, attachment `path`,
|
|
11
|
+
`contentId`, `disposition`, reply-to display names) are rejected loudly.
|
|
12
|
+
- Advertised capabilities: no repeated headers, native idempotency (an
|
|
13
|
+
`idempotencyKey` becomes Samva's `Idempotency-Key` header), no scheduling, and
|
|
14
|
+
expanded personalization.
|
|
15
|
+
- Failures become a redacted `EmailAdapterError` with `status`, `requestId`,
|
|
16
|
+
`retryable`, and `delivery` (`not_sent` or `unknown`); provider bodies and
|
|
17
|
+
credentials are never retained.
|
|
18
|
+
|
|
19
|
+
This package owns the Email SDK seam only. Samva owns delivery, templates,
|
|
20
|
+
events, and visibility. Keep the API key on the server; the adapter reads no
|
|
21
|
+
environment variables and loads no `.env` files.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arya Labs, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Provider-owned Samva community adapter for
|
|
4
4
|
[Email SDK](https://email-sdk.dev). It targets `@opencoredev/email-sdk@^1.1.0`
|
|
5
|
-
and `samva@^0.
|
|
5
|
+
and `samva@^0.5.0`.
|
|
6
6
|
|
|
7
7
|
This adapter is maintained by Samva as a community integration; it is not an
|
|
8
8
|
official built-in Email SDK adapter.
|
|
@@ -62,14 +62,14 @@ Samva Promise `client`. An API key is required unless a client is injected.
|
|
|
62
62
|
| In-memory attachments | Raw or base64 strings, `Uint8Array`, `ArrayBuffer`, and `Blob` become base64 with exact byte size. `contentType` is required. |
|
|
63
63
|
| `headers`, `tags`, `sendAt` | Rejected before the Samva client is called. |
|
|
64
64
|
| Attachment `path`, `contentId`, `disposition` | Rejected before the Samva client is called. URL attachment paths are not supported. |
|
|
65
|
-
| Send `idempotencyKey` |
|
|
65
|
+
| Send `idempotencyKey` | Forwarded as Samva's `Idempotency-Key` header. |
|
|
66
66
|
|
|
67
67
|
Capabilities are declared exactly as follows:
|
|
68
68
|
|
|
69
69
|
```ts
|
|
70
70
|
{
|
|
71
71
|
repeatedHeaders: false,
|
|
72
|
-
idempotency: "
|
|
72
|
+
idempotency: "native",
|
|
73
73
|
scheduling: false,
|
|
74
74
|
personalized: "expanded",
|
|
75
75
|
}
|
|
@@ -87,7 +87,8 @@ rejected delivery claims.
|
|
|
87
87
|
Unsupported or lossy input throws `EmailValidationError` without calling
|
|
88
88
|
Samva. API and transport failures become a redacted `EmailAdapterError` with
|
|
89
89
|
status and `x-request-id` when available. Retryability is enabled for statuses
|
|
90
|
-
408,
|
|
90
|
+
408, 425, 429, and 5xx; a `409 ConflictError` is non-retryable because replaying
|
|
91
|
+
the same request cannot succeed. Ambiguous transport, server, and malformed-success
|
|
91
92
|
outcomes use `delivery: "unknown"`; fallback therefore stops unless the
|
|
92
93
|
application explicitly opts into continuing after unknown delivery.
|
|
93
94
|
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { EmailAdapterError, EmailValidationError } from "@opencoredev/email-sdk";
|
|
2
2
|
import { createClient } from "samva";
|
|
3
|
-
|
|
4
3
|
//#region src/failures.ts
|
|
5
|
-
const knownNotSentTags = new Set([
|
|
4
|
+
const knownNotSentTags = /* @__PURE__ */ new Set([
|
|
6
5
|
"ConflictError",
|
|
7
6
|
"EmailChannelError",
|
|
8
7
|
"FlagDisabledError",
|
|
9
8
|
"ForbiddenError",
|
|
9
|
+
"OnboardingReviewRequiredError",
|
|
10
10
|
"PaymentRequiredError",
|
|
11
11
|
"RateLimitedError",
|
|
12
12
|
"ResourceNotFoundError",
|
|
@@ -34,7 +34,7 @@ function requestIdOf(error) {
|
|
|
34
34
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
35
35
|
}
|
|
36
36
|
function isRetryableStatus(status) {
|
|
37
|
-
return status === 408 || status ===
|
|
37
|
+
return status === 408 || status === 425 || status === 429 || status !== void 0 && status >= 500;
|
|
38
38
|
}
|
|
39
39
|
function deliveryOf(error) {
|
|
40
40
|
const tag = error["_tag"];
|
|
@@ -59,7 +59,6 @@ function toEmailAdapterError(error) {
|
|
|
59
59
|
delivery: deliveryOf(record)
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
|
|
63
62
|
//#endregion
|
|
64
63
|
//#region src/message.ts
|
|
65
64
|
const ordinaryEmail = /^[^\s@<>,]+@[^\s@<>,]+$/;
|
|
@@ -182,12 +181,11 @@ async function toSamvaMessage(message) {
|
|
|
182
181
|
...attachments.length === 0 ? {} : { attachments }
|
|
183
182
|
};
|
|
184
183
|
}
|
|
185
|
-
|
|
186
184
|
//#endregion
|
|
187
185
|
//#region src/samva.ts
|
|
188
186
|
const capabilities = {
|
|
189
187
|
repeatedHeaders: false,
|
|
190
|
-
idempotency: "
|
|
188
|
+
idempotency: "native",
|
|
191
189
|
scheduling: false,
|
|
192
190
|
personalized: "expanded"
|
|
193
191
|
};
|
|
@@ -216,12 +214,15 @@ function samva(options) {
|
|
|
216
214
|
validateMessage(message);
|
|
217
215
|
},
|
|
218
216
|
async send(message, context) {
|
|
219
|
-
if (context.idempotencyKey !== void 0) throw new EmailValidationError("The Samva Email SDK adapter does not support idempotency keys.");
|
|
220
217
|
if (context.signal?.aborted) throw abortReason(context.signal);
|
|
221
218
|
const payload = await toSamvaMessage(message);
|
|
219
|
+
const headers = context.idempotencyKey === void 0 ? void 0 : { "idempotency-key": context.idempotencyKey };
|
|
222
220
|
let result;
|
|
223
221
|
try {
|
|
224
|
-
result = await client.email.send(payload, {
|
|
222
|
+
result = await client.email.send(payload, {
|
|
223
|
+
...context.signal === void 0 ? {} : { signal: context.signal },
|
|
224
|
+
...headers === void 0 ? {} : { headers }
|
|
225
|
+
});
|
|
225
226
|
} catch (error) {
|
|
226
227
|
if (isAbortFailure(error, context.signal)) throw abortReason(context.signal);
|
|
227
228
|
throw toEmailAdapterError(error);
|
|
@@ -245,7 +246,7 @@ function samvaPlugin(options) {
|
|
|
245
246
|
adapters: [samva(options)]
|
|
246
247
|
};
|
|
247
248
|
}
|
|
248
|
-
|
|
249
249
|
//#endregion
|
|
250
250
|
export { samva, samvaPlugin };
|
|
251
|
+
|
|
251
252
|
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@samva/email-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Samva community adapter for Email SDK.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"email",
|
|
@@ -10,19 +10,22 @@
|
|
|
10
10
|
],
|
|
11
11
|
"homepage": "https://samva.dev",
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/
|
|
13
|
+
"url": "https://github.com/SamvaHQ/samva/issues"
|
|
14
14
|
},
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"author": "Arya Labs, Inc.",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/
|
|
19
|
+
"url": "git+https://github.com/SamvaHQ/samva.git",
|
|
20
20
|
"directory": "packages/email-sdk"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"dist",
|
|
24
|
+
"!dist/**/*.map",
|
|
25
|
+
"package.json",
|
|
24
26
|
"README.md",
|
|
25
|
-
"
|
|
27
|
+
"AGENTS.md",
|
|
28
|
+
"LICENSE"
|
|
26
29
|
],
|
|
27
30
|
"type": "module",
|
|
28
31
|
"sideEffects": false,
|
|
@@ -44,11 +47,11 @@
|
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
49
|
"@opencoredev/email-sdk": "^1.1.0",
|
|
47
|
-
"samva": "^0.
|
|
50
|
+
"samva": "^0.5.0"
|
|
48
51
|
},
|
|
49
52
|
"peerDependencies": {
|
|
50
53
|
"@opencoredev/email-sdk": "^1.1.0",
|
|
51
|
-
"samva": "^0.
|
|
54
|
+
"samva": "^0.5.0"
|
|
52
55
|
},
|
|
53
56
|
"engines": {
|
|
54
57
|
"node": ">=22"
|
package/dist/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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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"}
|