waha-shared 1.0.544 → 1.0.545
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 +4 -4
- package/dist/functions/resend.d.ts +25 -2
- package/dist/functions/resend.mjs +107 -20
- package/package.json +4 -11
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ data/languages/
|
|
|
37
37
|
|
|
38
38
|
## Type Generation Pipeline
|
|
39
39
|
|
|
40
|
-
Types flow through a pipeline managed by `
|
|
40
|
+
Types flow through a pipeline managed by `pnpm prep`:
|
|
41
41
|
|
|
42
42
|
```text
|
|
43
43
|
Zod schema (.zod.ts) and raw data file (.json)
|
|
@@ -47,12 +47,12 @@ Zod schema (.zod.ts) and raw data file (.json)
|
|
|
47
47
|
→ TypeScript index.ts
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
When you modify a `.zod.ts` file, run `
|
|
50
|
+
When you modify a `.zod.ts` file, run `pnpm prep` from the repo root to regenerate everything downstream.
|
|
51
51
|
|
|
52
52
|
## Building & Publishing
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
|
|
55
|
+
pnpm publish:latest
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
This will update the `dist` folder, commit changes, and publish to the npm registry.
|
|
@@ -64,4 +64,4 @@ The `dist/` directory is the build output — never edit files in it directly.
|
|
|
64
64
|
1. Create a new directory under `data/`, e.g. `data/myNewData/`
|
|
65
65
|
2. Add the raw JSON file: `myNewData.json`
|
|
66
66
|
3. Define the Zod schema: `myNewData.zod.ts`
|
|
67
|
-
4. Run `
|
|
67
|
+
4. Run `pnpm prep` to auto-generate the JSON schema, Python types, Python data, and index file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type ErrorResponse as ResendErrorResponse } from 'resend';
|
|
2
|
-
import { type DripEmailId, type DripLanguageId } from '../types/resend';
|
|
1
|
+
import { type Contact, type ListEmail, type ErrorResponse as ResendErrorResponse } from 'resend';
|
|
3
2
|
import type { DripSequenceId } from '../types/dripSequence';
|
|
3
|
+
import { type DripEmailId, type DripLanguageId } from '../types/resend';
|
|
4
4
|
/**
|
|
5
5
|
* Rate limits and server errors. Network failures (no status code) aren't
|
|
6
6
|
* retried: the request may have landed, and repeating a create or an event
|
|
@@ -79,11 +79,34 @@ export declare function createResendContact({ token, email, name, }: {
|
|
|
79
79
|
* `segmentId` is given. Read-only.
|
|
80
80
|
*/
|
|
81
81
|
export declare function listContactEmails(token: string, segmentId?: string): Promise<Set<string>>;
|
|
82
|
+
/**
|
|
83
|
+
* Every contact, or only those in one segment when `segmentId` is given. The
|
|
84
|
+
* list omits a contact's properties and topics; those take a call per contact.
|
|
85
|
+
* Read-only.
|
|
86
|
+
*/
|
|
87
|
+
export declare function listContacts(token: string, segmentId?: string): Promise<Contact[]>;
|
|
88
|
+
/** A contact's custom property values, by key. Read-only. */
|
|
89
|
+
export declare function getContactProperties(token: string, contactId: string): Promise<Record<string, string | number>>;
|
|
90
|
+
/**
|
|
91
|
+
* Every email sent since a time, newest first. Includes each recipient's copy
|
|
92
|
+
* of a broadcast as well as emails sent through the API and automations.
|
|
93
|
+
* Read-only.
|
|
94
|
+
*/
|
|
95
|
+
export declare function listEmailsSince(token: string, since: Date): Promise<ListEmail[]>;
|
|
82
96
|
/** Every segment's id and name. Read-only. */
|
|
83
97
|
export declare function listSegments(token: string): Promise<{
|
|
84
98
|
id: string;
|
|
85
99
|
name: string;
|
|
86
100
|
}[]>;
|
|
101
|
+
/** Every template's name, by template id. Read-only. */
|
|
102
|
+
export declare function listTemplateNames(token: string): Promise<Map<string, string>>;
|
|
103
|
+
/**
|
|
104
|
+
* The template behind each email automations sent since a time, by email id.
|
|
105
|
+
* An email's own record doesn't say, so this reads it from the send steps of
|
|
106
|
+
* every automation run that could have sent one since then: those still
|
|
107
|
+
* running, and those that finished since. Read-only.
|
|
108
|
+
*/
|
|
109
|
+
export declare function listAutomationEmailTemplates(token: string, since: Date): Promise<Map<string, string>>;
|
|
87
110
|
/** Creates a segment and returns its id. */
|
|
88
111
|
export declare function createSegment(token: string, name: string): Promise<string>;
|
|
89
112
|
/**
|
|
@@ -9,7 +9,10 @@ import "../chunks/chunk-5WXWDALL.mjs";
|
|
|
9
9
|
import "../chunks/chunk-5AVXA5JL.mjs";
|
|
10
10
|
|
|
11
11
|
// functions/resend.ts
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
Resend
|
|
14
|
+
} from "resend";
|
|
15
|
+
import z from "zod";
|
|
13
16
|
function isRetryable(error) {
|
|
14
17
|
const status = error.statusCode;
|
|
15
18
|
return status !== null && (status === 429 || status >= 500);
|
|
@@ -22,6 +25,19 @@ async function withRetry(call) {
|
|
|
22
25
|
await new Promise((resolve) => setTimeout(resolve, attempt * 500));
|
|
23
26
|
}
|
|
24
27
|
}
|
|
28
|
+
async function listAll(fetchPage, what) {
|
|
29
|
+
const items = [];
|
|
30
|
+
for (let after; ; ) {
|
|
31
|
+
const page = verify(
|
|
32
|
+
await withRetry(() => fetchPage(after)),
|
|
33
|
+
`Listing ${what} after ${after ?? "the start"}`
|
|
34
|
+
);
|
|
35
|
+
items.push(...page.data);
|
|
36
|
+
const last = page.data[page.data.length - 1];
|
|
37
|
+
if (!page.has_more || !last) return items;
|
|
38
|
+
after = last.id;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
25
41
|
function verify(result, context) {
|
|
26
42
|
if (result.error) throw new Error(`${context}: ${result.error.message}`);
|
|
27
43
|
if (!result.data) throw new Error(`${context}: Resend returned no data.`);
|
|
@@ -94,20 +110,47 @@ async function createResendContact({
|
|
|
94
110
|
).id;
|
|
95
111
|
}
|
|
96
112
|
async function listContactEmails(token, segmentId) {
|
|
113
|
+
const contacts = await listContacts(token, segmentId);
|
|
114
|
+
return new Set(contacts.map(({ email }) => lower(email)));
|
|
115
|
+
}
|
|
116
|
+
async function listContacts(token, segmentId) {
|
|
117
|
+
const resend = new Resend(token);
|
|
118
|
+
return listAll(
|
|
119
|
+
(after) => resend.contacts.list({
|
|
120
|
+
limit: 100,
|
|
121
|
+
...segmentId && { segmentId },
|
|
122
|
+
...after && { after }
|
|
123
|
+
}),
|
|
124
|
+
`contacts${segmentId ? ` in segment ${segmentId}` : ""}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
async function getContactProperties(token, contactId) {
|
|
128
|
+
const resend = new Resend(token);
|
|
129
|
+
const contact = verify(
|
|
130
|
+
await withRetry(() => resend.contacts.get(contactId)),
|
|
131
|
+
`Getting contact ${contactId}`
|
|
132
|
+
);
|
|
133
|
+
return Object.fromEntries(
|
|
134
|
+
Object.entries(contact.properties ?? {}).map(([key, { value }]) => [
|
|
135
|
+
key,
|
|
136
|
+
value
|
|
137
|
+
])
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
async function listEmailsSince(token, since) {
|
|
97
141
|
const resend = new Resend(token);
|
|
98
|
-
const emails =
|
|
142
|
+
const emails = [];
|
|
99
143
|
for (let after; ; ) {
|
|
100
144
|
const page = verify(
|
|
101
145
|
await withRetry(
|
|
102
|
-
() => resend.
|
|
103
|
-
limit: 100,
|
|
104
|
-
...segmentId && { segmentId },
|
|
105
|
-
...after && { after }
|
|
106
|
-
})
|
|
146
|
+
() => resend.emails.list({ limit: 100, ...after && { after } })
|
|
107
147
|
),
|
|
108
|
-
`Listing
|
|
148
|
+
`Listing emails after ${after ?? "the start"}`
|
|
109
149
|
);
|
|
110
|
-
for (const
|
|
150
|
+
for (const email of page.data) {
|
|
151
|
+
if (new Date(email.created_at) < since) return emails;
|
|
152
|
+
emails.push(email);
|
|
153
|
+
}
|
|
111
154
|
const last = page.data[page.data.length - 1];
|
|
112
155
|
if (!page.has_more || !last) return emails;
|
|
113
156
|
after = last.id;
|
|
@@ -115,19 +158,58 @@ async function listContactEmails(token, segmentId) {
|
|
|
115
158
|
}
|
|
116
159
|
async function listSegments(token) {
|
|
117
160
|
const resend = new Resend(token);
|
|
118
|
-
const segments =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
161
|
+
const segments = await listAll(
|
|
162
|
+
(after) => resend.segments.list({ limit: 100, ...after && { after } }),
|
|
163
|
+
"segments"
|
|
164
|
+
);
|
|
165
|
+
return segments.map(({ id, name }) => ({ id, name }));
|
|
166
|
+
}
|
|
167
|
+
async function listTemplateNames(token) {
|
|
168
|
+
const resend = new Resend(token);
|
|
169
|
+
const templates = await listAll(
|
|
170
|
+
(after) => resend.templates.list({ limit: 100, ...after && { after } }),
|
|
171
|
+
"templates"
|
|
172
|
+
);
|
|
173
|
+
return new Map(templates.map(({ id, name }) => [id, name]));
|
|
174
|
+
}
|
|
175
|
+
var SendEmailOutput = z.object({
|
|
176
|
+
email_id: z.string(),
|
|
177
|
+
template_id: z.string()
|
|
178
|
+
});
|
|
179
|
+
async function listAutomationEmailTemplates(token, since) {
|
|
180
|
+
const resend = new Resend(token);
|
|
181
|
+
const templates = /* @__PURE__ */ new Map();
|
|
182
|
+
const automations = await listAll(
|
|
183
|
+
(after) => resend.automations.list({ limit: 100, ...after && { after } }),
|
|
184
|
+
"automations"
|
|
185
|
+
);
|
|
186
|
+
for (const automation of automations) {
|
|
187
|
+
const runs = await listAll(
|
|
188
|
+
(after) => resend.automations.runs.list({
|
|
189
|
+
automationId: automation.id,
|
|
190
|
+
limit: 100,
|
|
191
|
+
...after && { after }
|
|
192
|
+
}),
|
|
193
|
+
`runs of automation ${automation.id}`
|
|
125
194
|
);
|
|
126
|
-
for (const { id,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
195
|
+
for (const { id: runId, status, completed_at } of runs) {
|
|
196
|
+
const mayHaveSent = status === "running" || !completed_at || new Date(completed_at) >= since;
|
|
197
|
+
if (!mayHaveSent) continue;
|
|
198
|
+
const run = verify(
|
|
199
|
+
await withRetry(
|
|
200
|
+
() => resend.automations.runs.get({ automationId: automation.id, runId })
|
|
201
|
+
),
|
|
202
|
+
`Getting run ${runId} of automation ${automation.id}`
|
|
203
|
+
);
|
|
204
|
+
for (const step of run.steps) {
|
|
205
|
+
if (step.type !== "send_email") continue;
|
|
206
|
+
const output = SendEmailOutput.safeParse(step.output);
|
|
207
|
+
if (output.success)
|
|
208
|
+
templates.set(output.data.email_id, output.data.template_id);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
130
211
|
}
|
|
212
|
+
return templates;
|
|
131
213
|
}
|
|
132
214
|
async function createSegment(token, name) {
|
|
133
215
|
const resend = new Resend(token);
|
|
@@ -321,10 +403,15 @@ export {
|
|
|
321
403
|
dripId,
|
|
322
404
|
enrollInDonorSequence,
|
|
323
405
|
enrollInDripSequence,
|
|
406
|
+
getContactProperties,
|
|
324
407
|
isDripLanguage,
|
|
325
408
|
isRetryable,
|
|
409
|
+
listAutomationEmailTemplates,
|
|
326
410
|
listContactEmails,
|
|
411
|
+
listContacts,
|
|
412
|
+
listEmailsSince,
|
|
327
413
|
listSegments,
|
|
414
|
+
listTemplateNames,
|
|
328
415
|
setSegmentMembership,
|
|
329
416
|
splitName,
|
|
330
417
|
subscribeToNewsletter,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "waha-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.545",
|
|
4
4
|
"author": "Waha",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@types/signale": "^1.4.7",
|
|
@@ -25,15 +25,8 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@crowdin/crowdin-api-client": "^1.59.2",
|
|
29
|
-
"@orpc/client": "^1.16.1",
|
|
30
|
-
"@orpc/contract": "^1.16.1",
|
|
31
|
-
"@orpc/openapi-client": "^1.16.1",
|
|
32
|
-
"@types/react": "^19.3.0",
|
|
33
28
|
"esbuild": "^0.28.2",
|
|
34
|
-
"
|
|
35
|
-
"typescript": "~5.9.2",
|
|
36
|
-
"zod": "^4.6.7"
|
|
29
|
+
"typescript": "~5.9.2"
|
|
37
30
|
},
|
|
38
31
|
"files": [
|
|
39
32
|
"dist",
|
|
@@ -43,7 +36,7 @@
|
|
|
43
36
|
"license": "MIT",
|
|
44
37
|
"scripts": {
|
|
45
38
|
"build": "node build.mts",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
39
|
+
"publish:latest": "bash publish.sh",
|
|
40
|
+
"publish:preview": "bash publish.sh --preview"
|
|
48
41
|
}
|
|
49
42
|
}
|