@productcraft/mail 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/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4277 -0
- package/dist/index.d.ts +4277 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ProductCraft
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @productcraft/mail
|
|
2
|
+
|
|
3
|
+
Typed Node.js SDK for [ProductCraft Mail](https://productcraft.co) — receive-and-store mail platform: send template-rendered messages, manage mailboxes + domains + DKIM, lint templates, track deliveries, configure outbound webhooks, hold suppression lists.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @productcraft/mail
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Server-side only. The SDK ships a Platform API Key in the `Authorization` header — never embed it in a browser bundle.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Mail } from "@productcraft/mail";
|
|
15
|
+
|
|
16
|
+
const mail = new Mail({
|
|
17
|
+
auth: { type: "apiKey", key: process.env.PCFT_KEY! },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Send a template-rendered message
|
|
21
|
+
const { data, error } = await mail.client.POST(
|
|
22
|
+
"/v1/workspaces/{workspace_id}/templates/{name}/send",
|
|
23
|
+
{
|
|
24
|
+
params: {
|
|
25
|
+
path: { workspace_id: "ws_...", name: "welcome" },
|
|
26
|
+
// Make retries safe — replays return the original response
|
|
27
|
+
// with `Idempotent-Replay: true` for 24h.
|
|
28
|
+
header: { "Idempotency-Key": "welcome-2026-05-22-alice" },
|
|
29
|
+
},
|
|
30
|
+
body: {
|
|
31
|
+
from: "hello@yourbrand.com",
|
|
32
|
+
to: "alice@example.com",
|
|
33
|
+
subject: "Welcome", // optional — falls back to template subject
|
|
34
|
+
data: { name: "Alice" }, // rendered into the template
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The `client` property is an [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/) instance bound to `https://api.mail.productcraft.co` and your auth credential. Every endpoint in the published OpenAPI spec is reachable through it — your editor's autocomplete lists paths + method shapes + body fields.
|
|
41
|
+
|
|
42
|
+
## Configuration
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
new Mail({
|
|
46
|
+
// The auth credential the SDK presents to Mail
|
|
47
|
+
auth: { type: "apiKey", key: "pcft_live_..." }
|
|
48
|
+
| { type: "bearer", token: "eyJ..." }
|
|
49
|
+
| { type: "cookie", value: "auth_token=..." },
|
|
50
|
+
// Override the prod base URL — useful for staging / a local proxy
|
|
51
|
+
baseUrl: "https://api.mail.example.test",
|
|
52
|
+
// Custom fetch implementation (undici with retry, mock in tests, ...)
|
|
53
|
+
fetch: customFetch,
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Common operations
|
|
58
|
+
|
|
59
|
+
Every path is workspace-scoped — `{workspace_id}` is the UUID of the workspace that owns the resource, returned by `@productcraft/platform-auth`'s introspect endpoint.
|
|
60
|
+
|
|
61
|
+
### Templates
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// List templates
|
|
65
|
+
await mail.client.GET(
|
|
66
|
+
"/v1/workspaces/{workspace_id}/templates",
|
|
67
|
+
{ params: { path: { workspace_id: "ws_..." } } },
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Lint a template before saving — the body field is `body_html`
|
|
71
|
+
// (wire) / `bodyHtml` (camelCase TS DTO), NOT `html`.
|
|
72
|
+
await mail.client.POST(
|
|
73
|
+
"/v1/workspaces/{workspace_id}/templates/lint",
|
|
74
|
+
{
|
|
75
|
+
params: { path: { workspace_id: "<workspace-uuid>" } },
|
|
76
|
+
body: { body_html: "<p>Hello {{name}}</p>" },
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
// → { score, findings: [...] }
|
|
80
|
+
|
|
81
|
+
// Preview / render with sample data
|
|
82
|
+
await mail.client.POST(
|
|
83
|
+
"/v1/workspaces/{workspace_id}/templates/{name}/render",
|
|
84
|
+
{
|
|
85
|
+
params: { path: { workspace_id: "ws_...", name: "welcome" } },
|
|
86
|
+
body: { data: { name: "Alice" } },
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Send to one address (template-rendered)
|
|
91
|
+
await mail.client.POST(
|
|
92
|
+
"/v1/workspaces/{workspace_id}/templates/{name}/send",
|
|
93
|
+
{ params: { ... }, body: { from, to, data } },
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Send the same template to many addresses (batch)
|
|
97
|
+
await mail.client.POST(
|
|
98
|
+
"/v1/workspaces/{workspace_id}/templates/{name}/send-batch",
|
|
99
|
+
{ params: { ... }, body: { recipients: [{ to, data }, ...] } },
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Domains + DKIM
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// List domains
|
|
107
|
+
await mail.client.GET("/v1/workspaces/{workspace_id}/domains", { ... });
|
|
108
|
+
|
|
109
|
+
// Add a domain (then publish the DKIM TXT record Mail prints)
|
|
110
|
+
await mail.client.POST(
|
|
111
|
+
"/v1/workspaces/{workspace_id}/domains",
|
|
112
|
+
{ params: { ... }, body: { name: "mail.yourbrand.com" } },
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Verify after DNS propagation
|
|
116
|
+
await mail.client.POST(
|
|
117
|
+
"/v1/workspaces/{workspace_id}/domains/{domain_id}/verify",
|
|
118
|
+
{ params: { path: { workspace_id, domain_id } } },
|
|
119
|
+
);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Messages
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// Workspace-wide message listing + filters
|
|
126
|
+
await mail.client.GET(
|
|
127
|
+
"/v1/workspaces/{workspace_id}/messages",
|
|
128
|
+
{ params: { path: { workspace_id }, query: { limit: 50 } } },
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Read body / attachments / raw RFC822
|
|
132
|
+
await mail.client.GET("/v1/workspaces/{workspace_id}/messages/{id}/body", { ... });
|
|
133
|
+
await mail.client.GET(
|
|
134
|
+
"/v1/workspaces/{workspace_id}/mailboxes/{id}/messages/{mid}/attachments/{position}",
|
|
135
|
+
{ ... },
|
|
136
|
+
);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Suppression list
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// Add an email to the workspace suppression list
|
|
143
|
+
await mail.client.POST(
|
|
144
|
+
"/v1/workspaces/{workspace_id}/suppression",
|
|
145
|
+
{ params: { ... }, body: { email: "bounced@example.com", reason: "hard_bounce" } },
|
|
146
|
+
);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Outbound webhooks
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
// Configure the workspace's default webhook (delivery / open / bounce events)
|
|
153
|
+
await mail.client.PUT(
|
|
154
|
+
"/v1/workspaces/{workspace_id}/webhooks/default",
|
|
155
|
+
{
|
|
156
|
+
params: { ... },
|
|
157
|
+
body: { url: "https://yourapp.com/hooks/mail", events: ["message.delivered"] },
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Per-domain webhook overrides + secret rotation
|
|
162
|
+
await mail.client.POST(
|
|
163
|
+
"/v1/workspaces/{workspace_id}/webhooks/domains/{domain_id}/rotate-secret",
|
|
164
|
+
{ ... },
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Idempotency
|
|
169
|
+
|
|
170
|
+
Send endpoints accept an `Idempotency-Key` header (1–256 chars, `[A-Za-z0-9_\-:]`). Retries with the same key + same body replay the original response with `Idempotent-Replay: true` for 24h. Same key + different body returns `409 IDEMPOTENCY_KEY_REUSE`.
|
|
171
|
+
|
|
172
|
+
## How this SDK is built
|
|
173
|
+
|
|
174
|
+
Generated from the live OpenAPI spec at `https://api.mail.productcraft.co/docs-json` via [`openapi-typescript`](https://openapi-ts.dev/) (types) + [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/) (runtime). The nightly `spec-refresh` workflow opens a PR whenever the spec changes; merging publishes a patch bump so type-safe consumers stay current automatically.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
[MIT](https://github.com/clauderanelagh/productcraft-node/blob/main/LICENSE).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@productcraft/core');
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var Mail = class {
|
|
7
|
+
/** The underlying typed client. v0 surface — every endpoint reachable. */
|
|
8
|
+
client;
|
|
9
|
+
constructor(config = {}) {
|
|
10
|
+
this.client = core.makeClient(core.PC_BASE_URL.mail, config);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
exports.Mail = Mail;
|
|
15
|
+
//# sourceMappingURL=index.cjs.map
|
|
16
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["makeClient","PC_BASE_URL"],"mappings":";;;;;AASO,IAAM,OAAN,MAAW;AAAA;AAAA,EAEA,MAAA;AAAA,EAEhB,WAAA,CAAY,MAAA,GAAyB,EAAC,EAAG;AACvC,IAAA,IAAA,CAAK,MAAA,GAASA,eAAA,CAAkBC,gBAAA,CAAY,IAAA,EAAM,MAAM,CAAA;AAAA,EAC1D;AACF","file":"index.cjs","sourcesContent":["import type { paths } from \"./_generated.js\";\nimport { makeClient, PC_BASE_URL, type PCClientConfig } from \"@productcraft/core\";\n\n/**\n * Mail — typed `openapi-fetch` client for every endpoint in\n * `Specs/mail.json`. Reach for `client.GET(\"/v1/...\")` or\n * `client.POST(...)`; request / response types come from the\n * generated `paths` interface.\n */\nexport class Mail {\n /** The underlying typed client. v0 surface — every endpoint reachable. */\n public readonly client: ReturnType<typeof makeClient<paths>>;\n\n constructor(config: PCClientConfig = {}) {\n this.client = makeClient<paths>(PC_BASE_URL.mail, config);\n }\n}\n\nexport type { paths } from \"./_generated.js\";\n"]}
|