@crvouga/mockingbird-service-resend 2.1.0 → 2.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/CHANGELOG.md +13 -4
- package/README.md +2 -0
- package/SUPPORT.md +5 -3
- package/dist/{chunk-JNTBSBDY.js → chunk-VMXQ2LLE.js} +142 -9
- package/dist/chunk-VMXQ2LLE.js.map +7 -0
- package/dist/{chunk-FT6SKLHQ.js → chunk-X3KXLVOT.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +7 -4
- package/dist/index.js +1 -1
- package/dist/server.d.ts +3 -0
- package/dist/server.js +2 -2
- package/openapi.yaml +232 -0
- package/package.json +1 -1
- package/dist/chunk-JNTBSBDY.js.map +0 -7
- /package/dist/{chunk-FT6SKLHQ.js.map → chunk-X3KXLVOT.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-resend
|
|
2
2
|
|
|
3
|
+
## 2.2.0 (2026-10-05)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- add scripted local API mock ([4a76a11](https://github.com/crvouga/mockingbird/commit/4a76a114e2efad45934e0a93cf29576fd418e15c))
|
|
8
|
+
- add email collection pagination ([c75ffb9](https://github.com/crvouga/mockingbird/commit/c75ffb9db5a04eff4965de8f378b931d56cd8d60))
|
|
9
|
+
- add email collection pagination ([ae926d0](https://github.com/crvouga/mockingbird/commit/ae926d0e91a283e7ad834711bf8e279916f79684))
|
|
10
|
+
|
|
11
|
+
### Fixes and improvements
|
|
12
|
+
|
|
13
|
+
- support embedded route parameters ([127cad9](https://github.com/crvouga/mockingbird/commit/127cad997ef106950c1b866070eea191ba54403f))
|
|
14
|
+
- preserve namespace URLs after main sync ([62a15ce](https://github.com/crvouga/mockingbird/commit/62a15ce9bd453069bde69e3a30c91efc01f8871b))
|
|
15
|
+
|
|
3
16
|
## 2.1.0 (2026-10-04)
|
|
4
17
|
|
|
5
18
|
### Features
|
|
@@ -12,10 +25,6 @@
|
|
|
12
25
|
- fill the embedded viewport ([c1a35a4](https://github.com/crvouga/mockingbird/commit/c1a35a4789389ee9cd06c286d09bd47c4d06c71e))
|
|
13
26
|
- make dialog cleanup immediate ([0c1a3c5](https://github.com/crvouga/mockingbird/commit/0c1a3c5b12584d50c19dcae595b951b8e5417b9c))
|
|
14
27
|
|
|
15
|
-
### Dependencies
|
|
16
|
-
|
|
17
|
-
- `@crvouga/mockingbird-service-sqlite`
|
|
18
|
-
|
|
19
28
|
## 2.0.0 (2026-10-04)
|
|
20
29
|
|
|
21
30
|
### ⚠️ Breaking changes
|
package/README.md
CHANGED
|
@@ -77,6 +77,8 @@ await mock.close()
|
|
|
77
77
|
| --- | --- |
|
|
78
78
|
| `POST /emails` | `{from, to, subject, html?, text?, cc?, bcc?, reply_to?, headers?, tags?, attachments?, scheduled_at?}` → `{id}` (a UUID). The SDK renders `react` to HTML before sending, so the mock sees HTML. Violations answer Resend's body `{statusCode, name, message}`: 422 `missing_required_field` (`Missing \`to\` field.`, or no `html`/`text`), 422 `validation_error` for a malformed address (`Invalid \`from\` field. The email address needs to follow …`), a tag outside `[A-Za-z0-9_-]`, or any other contract violation. `Idempotency-Key`: the same key and payload replay the first 200 byte for byte (`idempotent-replayed: true`); another payload is 409 `invalid_idempotent_request`; a key still in flight is 409 `concurrent_idempotent_requests`; a key outside 1–256 characters is 400 `invalid_idempotency_key`. Only 200s are remembered. |
|
|
79
79
|
| `GET /emails/{id}` | `emails.get`: `{object: "email", id, to, from, created_at, subject, html, text, cc, bcc, reply_to, last_event: "delivered" \| "scheduled", scheduled_at, headers, tags}`. `headers` keeps the names and values from the send; `to` keeps the original address form and order. |
|
|
80
|
+
| `GET /emails` | Newest-first sent email metadata, `{object: "list", has_more, data}`. `limit` defaults to 20 (1–100); `after`/`before` use email IDs and exclude the cursor. Content is available from the detail endpoint. |
|
|
81
|
+
| `GET /emails/receiving` | Newest-first received email metadata with the same envelope and ID cursors. Without `limit`, returns every received email; an explicit limit must be 1–100. Includes attachment metadata and size. |
|
|
80
82
|
| `GET /emails/receiving/{id}` | A received email: `{object: "email", id, to, from, cc, bcc, reply_to, created_at, subject, html, text, headers, message_id, attachments[{id, filename, content_type, content_disposition, content_id}]}`. |
|
|
81
83
|
| `GET /emails/receiving/{id}/attachments` | `{object: "list", has_more: false, data: [{id, filename, content_type, content_disposition, content_id, size, download_url, expires_at}]}`. |
|
|
82
84
|
| `GET /downloads/inbound/{attachment_id}` | The `download_url`: unauthenticated, the bytes with `content-type` and `content-length`. In a non-default namespace the URL carries `/__admin/ns/<name>`. |
|
package/SUPPORT.md
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Generated from `openapi.yaml`; do not edit by hand.
|
|
4
4
|
|
|
5
|
-
- operations in spec: **
|
|
6
|
-
- supported by the mock: **
|
|
7
|
-
- parity enabled: **
|
|
5
|
+
- operations in spec: **7**
|
|
6
|
+
- supported by the mock: **7**
|
|
7
|
+
- parity enabled: **7**
|
|
8
8
|
|
|
9
9
|
| operationId | route | mock | parity | notes |
|
|
10
10
|
| --- | --- | --- | --- | --- |
|
|
11
|
+
| `ListEmails` | `GET /emails` | ✅ supported | ✅ | |
|
|
11
12
|
| `SendEmail` | `POST /emails` | ✅ supported | ⚠️ unsafe (opt-in) | |
|
|
12
13
|
| `GetEmail` | `GET /emails/{email_id}` | ✅ supported | ✅ | |
|
|
14
|
+
| `ListReceivedEmails` | `GET /emails/receiving` | ✅ supported | ✅ | |
|
|
13
15
|
| `GetReceivedEmail` | `GET /emails/receiving/{email_id}` | ✅ supported | ✅ | |
|
|
14
16
|
| `ListReceivedEmailAttachments` | `GET /emails/receiving/{email_id}/attachments` | ✅ supported | ✅ | |
|
|
15
17
|
| `DownloadReceivedAttachment` | `GET /downloads/inbound/{attachment_id}` | ✅ supported | ✅ | |
|
|
@@ -1018,7 +1018,40 @@ var decodeBody = (contentType, bytes) => {
|
|
|
1018
1018
|
return { kind: "bytes", value: bytes };
|
|
1019
1019
|
};
|
|
1020
1020
|
var readBody = async (message) => {
|
|
1021
|
-
|
|
1021
|
+
if (!(message instanceof Request) || !message.body) {
|
|
1022
|
+
if (message instanceof Request)
|
|
1023
|
+
message.signal.throwIfAborted();
|
|
1024
|
+
return decodeBody(message.headers.get("content-type"), new Uint8Array(await message.arrayBuffer()));
|
|
1025
|
+
}
|
|
1026
|
+
message.signal.throwIfAborted();
|
|
1027
|
+
const reader = message.body.getReader();
|
|
1028
|
+
const cancel = () => {
|
|
1029
|
+
void reader.cancel(message.signal.reason).catch(() => {
|
|
1030
|
+
});
|
|
1031
|
+
};
|
|
1032
|
+
message.signal.addEventListener("abort", cancel, { once: true });
|
|
1033
|
+
const chunks = [];
|
|
1034
|
+
let size = 0;
|
|
1035
|
+
try {
|
|
1036
|
+
for (; ; ) {
|
|
1037
|
+
message.signal.throwIfAborted();
|
|
1038
|
+
const { done, value } = await reader.read();
|
|
1039
|
+
message.signal.throwIfAborted();
|
|
1040
|
+
if (done)
|
|
1041
|
+
break;
|
|
1042
|
+
chunks.push(value);
|
|
1043
|
+
size += value.byteLength;
|
|
1044
|
+
}
|
|
1045
|
+
} finally {
|
|
1046
|
+
message.signal.removeEventListener("abort", cancel);
|
|
1047
|
+
reader.releaseLock();
|
|
1048
|
+
}
|
|
1049
|
+
const bytes = new Uint8Array(size);
|
|
1050
|
+
let offset = 0;
|
|
1051
|
+
for (const chunk of chunks) {
|
|
1052
|
+
bytes.set(chunk, offset);
|
|
1053
|
+
offset += chunk.byteLength;
|
|
1054
|
+
}
|
|
1022
1055
|
return decodeBody(message.headers.get("content-type"), bytes);
|
|
1023
1056
|
};
|
|
1024
1057
|
|
|
@@ -1896,7 +1929,42 @@ var verifyOperations = (document2, handlers) => {
|
|
|
1896
1929
|
}
|
|
1897
1930
|
return problems;
|
|
1898
1931
|
};
|
|
1899
|
-
var
|
|
1932
|
+
var escapePattern = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1933
|
+
var serviceRoute = (template) => {
|
|
1934
|
+
const names = [];
|
|
1935
|
+
let pattern = "", end = 0;
|
|
1936
|
+
for (const match of template.matchAll(/\{([^}]+)\}/g)) {
|
|
1937
|
+
pattern += `${escapePattern(template.slice(end, match.index))}([^/]+?)`;
|
|
1938
|
+
const name = match[1];
|
|
1939
|
+
if (!name)
|
|
1940
|
+
throw new Error(`Invalid OpenAPI route template: ${template}`);
|
|
1941
|
+
names.push(name);
|
|
1942
|
+
end = match.index + match[0].length;
|
|
1943
|
+
}
|
|
1944
|
+
pattern += escapePattern(template.slice(end));
|
|
1945
|
+
const matcher = new RegExp(`^${pattern}$`);
|
|
1946
|
+
const path = template.split("/").map((segment, index) => {
|
|
1947
|
+
if (!segment.includes("{"))
|
|
1948
|
+
return segment;
|
|
1949
|
+
const literal = /^\{([^}]+)\}$/.exec(segment);
|
|
1950
|
+
if (literal)
|
|
1951
|
+
return `:${literal[1]}`;
|
|
1952
|
+
let expression = "", offset = 0;
|
|
1953
|
+
for (const match of segment.matchAll(/\{[^}]+\}/g)) {
|
|
1954
|
+
expression += `${escapePattern(segment.slice(offset, match.index))}[^/]+?`;
|
|
1955
|
+
offset = match.index + match[0].length;
|
|
1956
|
+
}
|
|
1957
|
+
expression += escapePattern(segment.slice(offset));
|
|
1958
|
+
return `:mockingbirdSegment${index}{${expression}}`;
|
|
1959
|
+
}).join("/");
|
|
1960
|
+
return {
|
|
1961
|
+
path,
|
|
1962
|
+
params: (pathname) => {
|
|
1963
|
+
const values = matcher.exec(pathname);
|
|
1964
|
+
return Object.fromEntries(names.map((name, index) => [name, decodeURIComponent(values?.[index + 1] ?? "")]));
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
};
|
|
1900
1968
|
var routeOrder = (a, b) => {
|
|
1901
1969
|
const sa = a.path.split("/");
|
|
1902
1970
|
const sb = b.path.split("/");
|
|
@@ -1931,6 +1999,7 @@ var createService = (options) => {
|
|
|
1931
1999
|
for (const operation of operations) {
|
|
1932
2000
|
const metadata = operationMetadata(operation.operation);
|
|
1933
2001
|
const handler = options.handlers[operation.operationId];
|
|
2002
|
+
const registeredRoute = serviceRoute(operation.path);
|
|
1934
2003
|
const route = async (c) => {
|
|
1935
2004
|
const request = c.req.raw;
|
|
1936
2005
|
if (!metadata.supported || !handler) {
|
|
@@ -1940,7 +2009,7 @@ var createService = (options) => {
|
|
|
1940
2009
|
const context = {
|
|
1941
2010
|
request,
|
|
1942
2011
|
url,
|
|
1943
|
-
params:
|
|
2012
|
+
params: registeredRoute.params(url.pathname),
|
|
1944
2013
|
query: queryOf(url),
|
|
1945
2014
|
body: await readBody(request),
|
|
1946
2015
|
sqlite: options.sqlite,
|
|
@@ -1952,7 +2021,7 @@ var createService = (options) => {
|
|
|
1952
2021
|
const short = await options.before?.(context);
|
|
1953
2022
|
return withIssues(request, short ?? await handler(context));
|
|
1954
2023
|
};
|
|
1955
|
-
app.on(operation.method.toUpperCase(),
|
|
2024
|
+
app.on(operation.method.toUpperCase(), registeredRoute.path, route);
|
|
1956
2025
|
}
|
|
1957
2026
|
return {
|
|
1958
2027
|
app,
|
|
@@ -2421,7 +2490,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
2421
2490
|
};
|
|
2422
2491
|
|
|
2423
2492
|
// ../core/dist/version.js
|
|
2424
|
-
var PACKAGE_VERSION = true ? "2.
|
|
2493
|
+
var PACKAGE_VERSION = true ? "2.2.0" : UNRELEASED_VERSION;
|
|
2425
2494
|
|
|
2426
2495
|
// ../core/dist/signing.js
|
|
2427
2496
|
var encoder = new TextEncoder();
|
|
@@ -3607,9 +3676,9 @@ var presetRoutes = (presets, runtime, adminPrefix) => ({
|
|
|
3607
3676
|
});
|
|
3608
3677
|
|
|
3609
3678
|
// src/generated/openapi.ts
|
|
3610
|
-
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Resend API (Mockingbird subset)","description":"Stateful mock subset of the Resend email API: send (with \`Idempotency-Key\`), retrieve a sent\\nemail, and the received-email (inbound) endpoints our care-chat hydration reads, including\\nthe attachment download URL. Trimmed from Resend's published API reference to what\\n\`resend@4.8.0\` and our backend call.\\n","version":"1","x-mockingbird-upstream":{"note":"Trimmed from https://resend.com/docs/api-reference (emails/send-email, emails/retrieve-email, receiving/retrieve-received-email, receiving/list-attachments); request/response shapes cross-checked against resend@4.8.0 (dist/index.mjs) and our consumer (send-emails.service.ts, notification-dispatcher email.ts, resend-inbound-email.client.ts)."}},"servers":[{"url":"https://api.resend.com"}],"security":[{"bearerAuth":[]}],"paths":{"/emails":{"post":{"operationId":"SendEmail","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","description":"Replays the first response for the same key and payload (1\u2013256 characters).","schema":{"type":"string","pattern":"^[!-~]{1,256}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailBody"}}}},"responses":{"200":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"email","identity":true},"x-mockingbird-volatile":{"kind":"id"}}}}}}},"400":{"description":"Invalid idempotency key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency key reused with another payload, or still in flight","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/emails/{email_id}":{"parameters":[{"name":"email_id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"email","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"GetEmail","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The sent email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Email"}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/emails/receiving/{email_id}":{"parameters":[{"$ref":"#/components/parameters/ReceivedEmailId"}],"get":{"operationId":"GetReceivedEmail","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The received email, with its content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceivedEmail"}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/emails/receiving/{email_id}/attachments":{"parameters":[{"$ref":"#/components/parameters/ReceivedEmailId"}],"get":{"operationId":"ListReceivedEmailAttachments","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Attachment metadata with short-lived download URLs","content":{"application/json":{"schema":{"type":"object","required":["object","data"],"properties":{"object":{"type":"string","enum":["list"]},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ReceivedAttachment"}}}}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/downloads/inbound/{attachment_id}":{"get":{"operationId":"DownloadReceivedAttachment","description":"The \`download_url\` of a received attachment: unauthenticated (it stands in for Resend's signed CDN URL), the raw bytes with their content type and length.","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"received_attachment","missing":"00000000-0000-4000-8000-000000000000"}}}],"responses":{"200":{"description":"The attachment bytes","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Unknown attachment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"\`Authorization: Bearer re_\u2026\`"}},"parameters":{"ReceivedEmailId":{"name":"email_id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"received_email","missing":"00000000-0000-4000-8000-000000000000"}}}},"schemas":{"Address":{"type":"string","description":"\`email@example.com\` or \`Name <email@example.com>\`","pattern":"^([^<>@]* <[A-Za-z0-9._+-]+@[A-Za-z0-9-]+(\\\\.[A-Za-z0-9-]+)+>|[A-Za-z0-9._+-]+@[A-Za-z0-9-]+(\\\\.[A-Za-z0-9-]+)+)$","maxLength":120},"Recipients":{"oneOf":[{"$ref":"#/components/schemas/Address"},{"type":"array","minItems":1,"maxItems":50,"items":{"$ref":"#/components/schemas/Address"}}]},"SendEmailBody":{"type":"object","required":["from","to","subject"],"properties":{"from":{"$ref":"#/components/schemas/Address"},"to":{"$ref":"#/components/schemas/Recipients"},"subject":{"type":"string","minLength":1,"maxLength":200},"html":{"type":"string","maxLength":5000},"text":{"type":"string","maxLength":5000},"cc":{"$ref":"#/components/schemas/Recipients"},"bcc":{"$ref":"#/components/schemas/Recipients"},"reply_to":{"$ref":"#/components/schemas/Recipients"},"scheduled_at":{"type":"string","maxLength":60},"headers":{"type":"object","additionalProperties":{"type":"string","maxLength":500}},"tags":{"type":"array","maxItems":10,"items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,256}$"},"value":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,256}$"}}}},"attachments":{"type":"array","maxItems":5,"items":{"type":"object","properties":{"filename":{"type":"string","minLength":1,"maxLength":100},"content":{"description":"Base64 bytes (or a JSON-serialised Node Buffer).","oneOf":[{"type":"string","maxLength":10000},{"type":"object","required":["type","data"],"properties":{"type":{"type":"string","enum":["Buffer"]},"data":{"type":"array","maxItems":10000,"items":{"type":"integer","minimum":0,"maximum":255}}}}]},"path":{"type":"string","maxLength":500},"content_type":{"type":"string","maxLength":100},"inline_content_id":{"type":"string","maxLength":100}}}}}},"Email":{"type":"object","required":["object","id","to","from","created_at","subject","html","text","bcc","cc","reply_to","last_event"],"properties":{"object":{"type":"string","enum":["email"]},"id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"to":{"type":"array","items":{"type":"string"}},"from":{"type":"string"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"subject":{"type":"string"},"html":{"type":["string","null"]},"text":{"type":["string","null"]},"bcc":{"type":["array","null"],"items":{"type":"string"}},"cc":{"type":["array","null"],"items":{"type":"string"}},"reply_to":{"type":["array","null"],"items":{"type":"string"}},"last_event":{"type":"string","enum":["delivered","scheduled"]},"scheduled_at":{"type":["string","null"]},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom headers from the send, with their original names and values."},"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}}}}},"ReceivedAttachment":{"type":"object","required":["id","filename","content_type","size","download_url"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"received_attachment","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"filename":{"type":"string"},"content_type":{"type":"string"},"content_disposition":{"type":["string","null"]},"content_id":{"type":["string","null"]},"size":{"type":"integer"},"download_url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"expires_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ReceivedEmail":{"type":"object","required":["object","id","to","from","created_at","subject","html","text","message_id","attachments"],"properties":{"object":{"type":"string","enum":["email"]},"id":{"type":"string","x-mockingbird-resource":{"type":"received_email","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"to":{"type":"array","items":{"type":"string"}},"from":{"type":"string"},"cc":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"reply_to":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"subject":{"type":["string","null"]},"html":{"type":["string","null"]},"text":{"type":["string","null"]},"headers":{"type":"object","additionalProperties":{"type":"string"}},"message_id":{"type":["string","null"]},"attachments":{"type":"array","items":{"type":"object","required":["id","filename","content_type"],"properties":{"id":{"type":"string"},"filename":{"type":"string"},"content_type":{"type":"string"},"content_disposition":{"type":["string","null"]},"content_id":{"type":["string","null"]}}}}}},"Error":{"type":"object","description":"Resend's error body; the SDK returns it verbatim as \`error\`.","required":["statusCode","name","message"],"properties":{"statusCode":{"type":"integer"},"name":{"type":"string"},"message":{"type":"string"}}}}}}`);
|
|
3611
|
-
var operationIds = ["SendEmail", "GetEmail", "GetReceivedEmail", "ListReceivedEmailAttachments", "DownloadReceivedAttachment"];
|
|
3612
|
-
var supportedOperationIds = ["SendEmail", "GetEmail", "GetReceivedEmail", "ListReceivedEmailAttachments", "DownloadReceivedAttachment"];
|
|
3679
|
+
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Resend API (Mockingbird subset)","description":"Stateful mock subset of the Resend email API: send (with \`Idempotency-Key\`), retrieve a sent\\nemail, and the received-email (inbound) endpoints our care-chat hydration reads, including\\nthe attachment download URL. Trimmed from Resend's published API reference to what\\n\`resend@4.8.0\` and our backend call.\\n","version":"1","x-mockingbird-upstream":{"note":"Trimmed from https://resend.com/docs/api-reference (emails/send-email, emails/retrieve-email, receiving/retrieve-received-email, receiving/list-attachments); request/response shapes cross-checked against resend@4.8.0 (dist/index.mjs) and our consumer (send-emails.service.ts, notification-dispatcher email.ts, resend-inbound-email.client.ts)."}},"servers":[{"url":"https://api.resend.com"}],"security":[{"bearerAuth":[]}],"paths":{"/emails":{"get":{"operationId":"ListEmails","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"after","in":"query","schema":{"type":"string"}},{"name":"before","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Newest-first email metadata","content":{"application/json":{"schema":{"type":"object","required":["object","has_more","data"],"properties":{"object":{"type":"string","enum":["list"]},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/SentEmailMetadata"}}}}}}},"401":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"SendEmail","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","description":"Replays the first response for the same key and payload (1\u2013256 characters).","schema":{"type":"string","pattern":"^[!-~]{1,256}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailBody"}}}},"responses":{"200":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"email","identity":true},"x-mockingbird-volatile":{"kind":"id"}}}}}}},"400":{"description":"Invalid idempotency key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Idempotency key reused with another payload, or still in flight","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/emails/{email_id}":{"parameters":[{"name":"email_id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"email","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"GetEmail","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The sent email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Email"}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/emails/receiving":{"get":{"operationId":"ListReceivedEmails","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"after","in":"query","schema":{"type":"string"}},{"name":"before","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Newest-first email metadata","content":{"application/json":{"schema":{"type":"object","required":["object","has_more","data"],"properties":{"object":{"type":"string","enum":["list"]},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ReceivedEmailMetadata"}}}}}}},"401":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/emails/receiving/{email_id}":{"parameters":[{"$ref":"#/components/parameters/ReceivedEmailId"}],"get":{"operationId":"GetReceivedEmail","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The received email, with its content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceivedEmail"}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/emails/receiving/{email_id}/attachments":{"parameters":[{"$ref":"#/components/parameters/ReceivedEmailId"}],"get":{"operationId":"ListReceivedEmailAttachments","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Attachment metadata with short-lived download URLs","content":{"application/json":{"schema":{"type":"object","required":["object","data"],"properties":{"object":{"type":"string","enum":["list"]},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ReceivedAttachment"}}}}}}},"401":{"description":"Missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown email","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/downloads/inbound/{attachment_id}":{"get":{"operationId":"DownloadReceivedAttachment","description":"The \`download_url\` of a received attachment: unauthenticated (it stands in for Resend's signed CDN URL), the raw bytes with their content type and length.","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"received_attachment","missing":"00000000-0000-4000-8000-000000000000"}}}],"responses":{"200":{"description":"The attachment bytes","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Unknown attachment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"\`Authorization: Bearer re_\u2026\`"}},"parameters":{"ReceivedEmailId":{"name":"email_id","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"received_email","missing":"00000000-0000-4000-8000-000000000000"}}}},"schemas":{"Address":{"type":"string","description":"\`email@example.com\` or \`Name <email@example.com>\`","pattern":"^([^<>@]* <[A-Za-z0-9._+-]+@[A-Za-z0-9-]+(\\\\.[A-Za-z0-9-]+)+>|[A-Za-z0-9._+-]+@[A-Za-z0-9-]+(\\\\.[A-Za-z0-9-]+)+)$","maxLength":120},"Recipients":{"oneOf":[{"$ref":"#/components/schemas/Address"},{"type":"array","minItems":1,"maxItems":50,"items":{"$ref":"#/components/schemas/Address"}}]},"SendEmailBody":{"type":"object","required":["from","to","subject"],"properties":{"from":{"$ref":"#/components/schemas/Address"},"to":{"$ref":"#/components/schemas/Recipients"},"subject":{"type":"string","minLength":1,"maxLength":200},"html":{"type":"string","maxLength":5000},"text":{"type":"string","maxLength":5000},"cc":{"$ref":"#/components/schemas/Recipients"},"bcc":{"$ref":"#/components/schemas/Recipients"},"reply_to":{"$ref":"#/components/schemas/Recipients"},"scheduled_at":{"type":"string","maxLength":60},"headers":{"type":"object","additionalProperties":{"type":"string","maxLength":500}},"tags":{"type":"array","maxItems":10,"items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,256}$"},"value":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,256}$"}}}},"attachments":{"type":"array","maxItems":5,"items":{"type":"object","properties":{"filename":{"type":"string","minLength":1,"maxLength":100},"content":{"description":"Base64 bytes (or a JSON-serialised Node Buffer).","oneOf":[{"type":"string","maxLength":10000},{"type":"object","required":["type","data"],"properties":{"type":{"type":"string","enum":["Buffer"]},"data":{"type":"array","maxItems":10000,"items":{"type":"integer","minimum":0,"maximum":255}}}}]},"path":{"type":"string","maxLength":500},"content_type":{"type":"string","maxLength":100},"inline_content_id":{"type":"string","maxLength":100}}}}}},"SentEmailMetadata":{"type":"object","required":["id","to","from","created_at","subject","bcc","cc","reply_to","message_id","last_event","scheduled_at"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"email","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"to":{"type":"array","items":{"type":"string"}},"from":{"type":"string"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"subject":{"type":"string"},"message_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"bcc":{"type":["array","null"],"items":{"type":"string"}},"cc":{"type":["array","null"],"items":{"type":"string"}},"reply_to":{"type":["array","null"],"items":{"type":"string"}},"last_event":{"type":"string","enum":["delivered","scheduled"]},"scheduled_at":{"type":["string","null"]}}},"ReceivedEmailMetadata":{"type":"object","required":["id","to","from","created_at","subject","bcc","cc","reply_to","message_id","attachments"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"received_email","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"to":{"type":"array","items":{"type":"string"}},"from":{"type":"string"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"subject":{"type":["string","null"]},"message_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"bcc":{"type":"array","items":{"type":"string"}},"cc":{"type":"array","items":{"type":"string"}},"reply_to":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array","items":{"type":"object","required":["id","filename","content_type","content_id","content_disposition","size"],"properties":{"id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"filename":{"type":"string"},"content_type":{"type":"string"},"content_id":{"type":["string","null"]},"content_disposition":{"type":["string","null"]},"size":{"type":"integer"}}}}}},"Email":{"type":"object","required":["object","id","to","from","created_at","subject","html","text","bcc","cc","reply_to","last_event"],"properties":{"object":{"type":"string","enum":["email"]},"id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"to":{"type":"array","items":{"type":"string"}},"from":{"type":"string"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"subject":{"type":"string"},"html":{"type":["string","null"]},"text":{"type":["string","null"]},"bcc":{"type":["array","null"],"items":{"type":"string"}},"cc":{"type":["array","null"],"items":{"type":"string"}},"reply_to":{"type":["array","null"],"items":{"type":"string"}},"last_event":{"type":"string","enum":["delivered","scheduled"]},"scheduled_at":{"type":["string","null"]},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom headers from the send, with their original names and values."},"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}}}}},"ReceivedAttachment":{"type":"object","required":["id","filename","content_type","size","download_url"],"properties":{"id":{"type":"string","x-mockingbird-resource":{"type":"received_attachment","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"filename":{"type":"string"},"content_type":{"type":"string"},"content_disposition":{"type":["string","null"]},"content_id":{"type":["string","null"]},"size":{"type":"integer"},"download_url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"expires_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ReceivedEmail":{"type":"object","required":["object","id","to","from","created_at","subject","html","text","message_id","attachments"],"properties":{"object":{"type":"string","enum":["email"]},"id":{"type":"string","x-mockingbird-resource":{"type":"received_email","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"to":{"type":"array","items":{"type":"string"}},"from":{"type":"string"},"cc":{"type":"array","items":{"type":"string"}},"bcc":{"type":"array","items":{"type":"string"}},"reply_to":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"subject":{"type":["string","null"]},"html":{"type":["string","null"]},"text":{"type":["string","null"]},"headers":{"type":"object","additionalProperties":{"type":"string"}},"message_id":{"type":["string","null"]},"attachments":{"type":"array","items":{"type":"object","required":["id","filename","content_type"],"properties":{"id":{"type":"string"},"filename":{"type":"string"},"content_type":{"type":"string"},"content_disposition":{"type":["string","null"]},"content_id":{"type":["string","null"]}}}}}},"Error":{"type":"object","description":"Resend's error body; the SDK returns it verbatim as \`error\`.","required":["statusCode","name","message"],"properties":{"statusCode":{"type":"integer"},"name":{"type":"string"},"message":{"type":"string"}}}}}}`);
|
|
3680
|
+
var operationIds = ["ListEmails", "SendEmail", "GetEmail", "ListReceivedEmails", "GetReceivedEmail", "ListReceivedEmailAttachments", "DownloadReceivedAttachment"];
|
|
3681
|
+
var supportedOperationIds = ["ListEmails", "SendEmail", "GetEmail", "ListReceivedEmails", "GetReceivedEmail", "ListReceivedEmailAttachments", "DownloadReceivedAttachment"];
|
|
3613
3682
|
|
|
3614
3683
|
// src/state.ts
|
|
3615
3684
|
var ResendState = class {
|
|
@@ -3983,6 +4052,8 @@ var ResendAPI = class {
|
|
|
3983
4052
|
this.onOutcome = options.onOutcome;
|
|
3984
4053
|
this.state = new ResendState(sqlite, namespace);
|
|
3985
4054
|
const handlers = defineOperations({
|
|
4055
|
+
ListEmails: (context) => this.listEmails(context),
|
|
4056
|
+
ListReceivedEmails: (context) => this.listReceivedEmails(context),
|
|
3986
4057
|
SendEmail: (context) => this.send(context),
|
|
3987
4058
|
GetEmail: (context) => this.getEmail(context),
|
|
3988
4059
|
GetReceivedEmail: (context) => this.getReceived(context),
|
|
@@ -4156,6 +4227,68 @@ var ResendAPI = class {
|
|
|
4156
4227
|
{ ids: { emailId: email.id } }
|
|
4157
4228
|
);
|
|
4158
4229
|
}
|
|
4230
|
+
emailPage(context, records, defaultLimit) {
|
|
4231
|
+
const query = context.url.searchParams;
|
|
4232
|
+
const rawLimit = query.get("limit");
|
|
4233
|
+
const limit = rawLimit === null ? Math.max(1, defaultLimit) : Number(rawLimit);
|
|
4234
|
+
const reject = (message) => {
|
|
4235
|
+
throw new HttpError(422, { statusCode: 422, name: "validation_error", message });
|
|
4236
|
+
};
|
|
4237
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || rawLimit !== null && limit > 100) {
|
|
4238
|
+
reject("The pagination limit must be a number between 1 and 100.");
|
|
4239
|
+
}
|
|
4240
|
+
const after = query.get("after");
|
|
4241
|
+
const before = query.get("before");
|
|
4242
|
+
if (after !== null && before !== null) reject("Provide either before or after, not both.");
|
|
4243
|
+
let selected = records;
|
|
4244
|
+
const cursor = after ?? before;
|
|
4245
|
+
if (cursor !== null) {
|
|
4246
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(cursor)) {
|
|
4247
|
+
reject("Invalid pagination cursor format.");
|
|
4248
|
+
}
|
|
4249
|
+
const index = records.findIndex((email) => email.id === cursor);
|
|
4250
|
+
selected = index < 0 ? [] : after !== null ? records.slice(index + 1) : records.slice(0, index);
|
|
4251
|
+
}
|
|
4252
|
+
return {
|
|
4253
|
+
object: "list",
|
|
4254
|
+
has_more: selected.length > limit,
|
|
4255
|
+
data: before === null ? selected.slice(0, limit) : selected.slice(-limit)
|
|
4256
|
+
};
|
|
4257
|
+
}
|
|
4258
|
+
listEmails(context) {
|
|
4259
|
+
const records = this.state.outbox.list().reverse().map((email) => ({
|
|
4260
|
+
id: email.id,
|
|
4261
|
+
message_id: `<${email.id}@mock.resend.test>`,
|
|
4262
|
+
to: email.toHeader,
|
|
4263
|
+
from: email.from,
|
|
4264
|
+
created_at: email.createdAt,
|
|
4265
|
+
subject: email.subject,
|
|
4266
|
+
bcc: email.bcc.length > 0 ? email.bcc : null,
|
|
4267
|
+
cc: email.cc.length > 0 ? email.cc : null,
|
|
4268
|
+
reply_to: email.replyTo.length > 0 ? email.replyTo : null,
|
|
4269
|
+
last_event: email.scheduledAt ? "scheduled" : "delivered",
|
|
4270
|
+
scheduled_at: email.scheduledAt
|
|
4271
|
+
}));
|
|
4272
|
+
return jsonRes(200, this.emailPage(context, records, 20));
|
|
4273
|
+
}
|
|
4274
|
+
listReceivedEmails(context) {
|
|
4275
|
+
const records = this.state.received.list({ order: "newest" }).map(({ value: email }) => ({
|
|
4276
|
+
id: email.id,
|
|
4277
|
+
to: email.to,
|
|
4278
|
+
from: email.from,
|
|
4279
|
+
created_at: email.createdAt,
|
|
4280
|
+
subject: email.subject,
|
|
4281
|
+
bcc: email.bcc,
|
|
4282
|
+
cc: email.cc,
|
|
4283
|
+
reply_to: email.replyTo,
|
|
4284
|
+
message_id: email.messageId,
|
|
4285
|
+
attachments: email.attachments.map((attachment) => ({
|
|
4286
|
+
...this.attachmentMeta(attachment),
|
|
4287
|
+
size: attachment.size
|
|
4288
|
+
}))
|
|
4289
|
+
}));
|
|
4290
|
+
return jsonRes(200, this.emailPage(context, records, records.length));
|
|
4291
|
+
}
|
|
4159
4292
|
/** Store an inbound email and build the `email.received` event for it. */
|
|
4160
4293
|
receive(input, origin) {
|
|
4161
4294
|
const id = this.state.nextId("received");
|
|
@@ -4304,4 +4437,4 @@ export {
|
|
|
4304
4437
|
bareAddress,
|
|
4305
4438
|
ResendAPI
|
|
4306
4439
|
};
|
|
4307
|
-
//# sourceMappingURL=chunk-
|
|
4440
|
+
//# sourceMappingURL=chunk-VMXQ2LLE.js.map
|