waha-shared 1.0.544 → 1.0.546

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 CHANGED
@@ -37,7 +37,7 @@ data/languages/
37
37
 
38
38
  ## Type Generation Pipeline
39
39
 
40
- Types flow through a pipeline managed by `npm run prep`:
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 `npm run prep` from the repo root to regenerate everything downstream.
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
- npm run update
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 `npm run prep` to auto-generate the JSON schema, Python types, Python data, and index file
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 { Resend } from "resend";
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 = /* @__PURE__ */ new Set();
142
+ const emails = [];
99
143
  for (let after; ; ) {
100
144
  const page = verify(
101
145
  await withRetry(
102
- () => resend.contacts.list({
103
- limit: 100,
104
- ...segmentId && { segmentId },
105
- ...after && { after }
106
- })
146
+ () => resend.emails.list({ limit: 100, ...after && { after } })
107
147
  ),
108
- `Listing contacts${segmentId ? ` in segment ${segmentId}` : ""} after ${after ?? "the start"}`
148
+ `Listing emails after ${after ?? "the start"}`
109
149
  );
110
- for (const contact of page.data) emails.add(lower(contact.email));
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
- for (let after; ; ) {
120
- const page = verify(
121
- await withRetry(
122
- () => resend.segments.list({ limit: 100, ...after && { after } })
123
- ),
124
- `Listing segments after ${after ?? "the start"}`
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, name } of page.data) segments.push({ id, name });
127
- const last = page.data[page.data.length - 1];
128
- if (!page.has_more || !last) return segments;
129
- after = last.id;
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.544",
3
+ "version": "1.0.546",
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
- "react": "^19.3.1",
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
- "update": "bash publish.sh",
47
- "update:preview": "bash publish.sh --preview"
39
+ "publish:latest": "bash publish.sh",
40
+ "publish:preview": "bash publish.sh --preview"
48
41
  }
49
42
  }