@openemail/sdk 0.0.2 → 0.0.4

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.
Files changed (5) hide show
  1. package/index.cjs +112 -8
  2. package/index.d.cts +260 -65
  3. package/index.d.ts +260 -65
  4. package/index.js +111 -9
  5. package/package.json +1 -1
package/index.cjs CHANGED
@@ -10,8 +10,20 @@ const BROADCAST_STATUSES = {
10
10
  CANCELLED: 'cancelled',
11
11
  FAILED: 'failed'
12
12
  };
13
+ const BROADCAST_RECIPIENT_FILTERS = {
14
+ PENDING: 'pending',
15
+ SENT: 'sent',
16
+ DELIVERED: 'delivered',
17
+ OPENED: 'opened',
18
+ NOT_OPENED: 'not_opened',
19
+ CLICKED: 'clicked',
20
+ BOUNCED: 'bounced',
21
+ COMPLAINED: 'complained',
22
+ FAILED: 'failed',
23
+ UNSUBSCRIBED: 'unsubscribed'
24
+ };
13
25
 
14
- var version = "0.0.2";
26
+ var version = "0.0.4";
15
27
 
16
28
  const BUILD_BASE_URL = 'https://api.openemail.uk';
17
29
 
@@ -19,6 +31,7 @@ const DEFAULTS = {
19
31
  BASE_URL: BUILD_BASE_URL,
20
32
  MAX_RETRIES: 2,
21
33
  TIMEOUT_MS: 30_000,
34
+ UPLOAD_TIMEOUT_MS: 600_000,
22
35
  USER_AGENT: `openemail-sdk/${version}`
23
36
  };
24
37
  const BROWSER_REFUSAL = [
@@ -51,6 +64,7 @@ const CLIENT_MESSAGES = {
51
64
  BASE_URL_SHAPE: 'is not a usable base URL. Pass an origin such as "https://api.openemail.uk", or the http origin of a server you run yourself.',
52
65
  NO_FETCH: 'No global fetch is available. Pass one as { fetch }, or run on Node 20+, Bun, Deno, Cloudflare Workers or a browser.',
53
66
  EMPTY_SEGMENT: 'An id must not be empty.',
67
+ FILENAME_REQUIRED: 'A file name is required. Pass it as { filename }, the name the file is stored and downloaded under.',
54
68
  DOT_SEGMENT: 'is not a usable id: a path segment made only of dots is removed by URL parsers, so the request would reach a different endpoint.'
55
69
  };
56
70
 
@@ -132,7 +146,14 @@ const FILE_KINDS = {
132
146
  };
133
147
  const FILE_DIRECTIONS = {
134
148
  INBOUND: 'inbound',
135
- OUTBOUND: 'outbound'
149
+ OUTBOUND: 'outbound',
150
+ UPLOADED: 'uploaded'
151
+ };
152
+ const FILE_USAGES = {
153
+ RECEIVED: 'received',
154
+ SENT: 'sent',
155
+ LINKED: 'linked',
156
+ SCHEDULED: 'scheduled'
136
157
  };
137
158
 
138
159
  const HTTP_METHODS = {
@@ -445,6 +466,8 @@ const QUERY_KEYS = {
445
466
  REASSIGN_TO: 'reassignTo',
446
467
  SOURCE: 'source',
447
468
  ADDRESS: 'address',
469
+ DIRECTION: 'direction',
470
+ FILTER: 'filter',
448
471
  SEARCH: 'search',
449
472
  SORT: 'sort',
450
473
  PAGE: 'page',
@@ -466,7 +489,8 @@ const QUERY_KEYS = {
466
489
  KEY_IDS: 'keyIds',
467
490
  SINCE: 'since',
468
491
  UNTIL: 'until',
469
- FAILED_ONLY: 'failedOnly'
492
+ FAILED_ONLY: 'failedOnly',
493
+ FILENAME: 'filename'
470
494
  };
471
495
  const LIST_SEPARATOR = ',';
472
496
 
@@ -517,6 +541,8 @@ const PATHS = {
517
541
  MEMBERS_INVITATIONS: '/members/invitations',
518
542
  SUPPRESSIONS: '/suppressions',
519
543
  FILES: '/files',
544
+ FILES_STATS: '/files/stats',
545
+ FILES_BATCH_DELETE: '/files/batch-delete',
520
546
  TEMP_MAIL_DOMAINS: '/temp-mail/domains',
521
547
  TEMP_MAIL_INBOXES: '/temp-mail/inboxes'
522
548
  };
@@ -555,8 +581,10 @@ const SEGMENTS = {
555
581
  REVOKE: 'revoke',
556
582
  ROTATE: 'rotate',
557
583
  ROTATE_SECRET: 'rotate-secret',
584
+ RECIPIENTS: 'recipients',
558
585
  SEND: 'send',
559
586
  SENDS: 'sends',
587
+ STATS: 'stats',
560
588
  SNOOZE: 'snooze',
561
589
  START: 'start',
562
590
  TEST: 'test',
@@ -631,6 +659,8 @@ const API_SCOPES = {
631
659
  DRAFTS_WRITE: 'drafts:write',
632
660
  THREADS_READ: 'threads:read',
633
661
  THREADS_WRITE: 'threads:write',
662
+ FILES_READ: 'files:read',
663
+ FILES_WRITE: 'files:write',
634
664
  LABELS_READ: 'labels:read',
635
665
  LABELS_WRITE: 'labels:write',
636
666
  CONTACTS_READ: 'contacts:read',
@@ -692,7 +722,9 @@ const WEBHOOK_EVENTS = {
692
722
  DOMAIN_SENDING_CHANGED: 'domain.sending_changed',
693
723
  DOMAIN_DELETED: 'domain.deleted',
694
724
  SUPPRESSION_ADDED: 'suppression.added',
695
- SUPPRESSION_REMOVED: 'suppression.removed'
725
+ SUPPRESSION_REMOVED: 'suppression.removed',
726
+ FILE_UPLOADED: 'file.uploaded',
727
+ FILE_DELETED: 'file.deleted'
696
728
  };
697
729
  const WEBHOOK_REPLAY_ERROR_CODES = {
698
730
  RESOURCE_NOT_FOUND: 'resource_not_found',
@@ -1148,7 +1180,7 @@ class Transport {
1148
1180
  const repeatable = options.repeatable ?? RETRYABLE_METHODS.has(method);
1149
1181
  let attempt = 0;
1150
1182
  for (;;) {
1151
- const outcome = await this.attempt(url, method, headers, body, options.signal, options.binary === true);
1183
+ const outcome = await this.attempt(url, method, headers, body, options.signal, options.binary === true, options.timeoutMs ?? this.timeoutMs);
1152
1184
  if (outcome instanceof OpenEmailNetworkError) {
1153
1185
  if (options.signal?.aborted || !repeatable || attempt >= this.maxRetries)
1154
1186
  throw outcome;
@@ -1195,13 +1227,13 @@ class Transport {
1195
1227
  headers[HEADER_KEYS.IDEMPOTENCY_KEY] = options.idempotencyKey ?? IdempotencyKey();
1196
1228
  return headers;
1197
1229
  }
1198
- async attempt(url, method, headers, body, signal, binary) {
1199
- const deadline = Deadline(this.timeoutMs, signal);
1230
+ async attempt(url, method, headers, body, signal, binary, timeoutMs) {
1231
+ const deadline = Deadline(timeoutMs, signal);
1200
1232
  const Failure = (error, fallback) => {
1201
1233
  if (signal?.aborted)
1202
1234
  return new OpenEmailNetworkError('Request aborted by the caller.', error);
1203
1235
  if (deadline.timedOut())
1204
- return new OpenEmailNetworkError(`Request timed out after ${this.timeoutMs}ms.`, error, true);
1236
+ return new OpenEmailNetworkError(`Request timed out after ${timeoutMs}ms.`, error, true);
1205
1237
  return new OpenEmailNetworkError(`${fallback}: ${Describe(error)}`, error);
1206
1238
  };
1207
1239
  try {
@@ -1308,6 +1340,7 @@ const AudienceContactsChildPath = (id, segment) => `${AudienceContactsPath(id)}/
1308
1340
  const AudienceContactPath = (id, email) => `${AudienceContactsPath(id)}/${Segment(email)}`;
1309
1341
  const BroadcastPath = (id) => `${PATHS.BROADCASTS}/${Segment(id)}`;
1310
1342
  const BroadcastChildPath = (id, segment) => `${BroadcastPath(id)}/${segment}`;
1343
+ const BroadcastRecipientPath = (id, emailId) => `${BroadcastChildPath(id, SEGMENTS.RECIPIENTS)}/${Segment(emailId)}`;
1311
1344
  const DomainPath = (id) => `${PATHS.DOMAINS}/${Segment(id)}`;
1312
1345
  const WebhookPath = (id) => `${PATHS.WEBHOOKS}/${Segment(id)}`;
1313
1346
  const WebhookChildPath = (id, segment) => `${WebhookPath(id)}/${segment}`;
@@ -1340,6 +1373,7 @@ const MemberInvitationResendPath = (id) => `${MemberInvitationPath(id)}/${SEGMEN
1340
1373
  const SuppressionPath = (id) => `${PATHS.SUPPRESSIONS}/${Segment(id)}`;
1341
1374
  const FilePath = (id) => `${PATHS.FILES}/${Segment(id)}`;
1342
1375
  const FileChildPath = (id, segment) => `${FilePath(id)}/${segment}`;
1376
+ const FileLinkPath = (id, linkId) => `${FileChildPath(id, SEGMENTS.LINKS)}/${Segment(linkId)}`;
1343
1377
  const TempInboxPath = (id) => `${PATHS.TEMP_MAIL_INBOXES}/${Segment(id)}`;
1344
1378
  const TempInboxExtendPath = (id) => `${TempInboxPath(id)}/${SEGMENTS.EXTEND}`;
1345
1379
  const TempMessagesPath = (id) => `${TempInboxPath(id)}/${SEGMENTS.MESSAGES}`;
@@ -1502,6 +1536,11 @@ const ToInstant = Instant;
1502
1536
  const ListQuery$a = (options) => ({
1503
1537
  [QUERY_KEYS.AUDIENCE_ID]: options.audienceId
1504
1538
  });
1539
+ const RecipientQuery = (options) => ({
1540
+ [QUERY_KEYS.FILTER]: options.filter,
1541
+ [QUERY_KEYS.Q]: options.q
1542
+ });
1543
+ const RecipientsPath = (id) => BroadcastChildPath(id, SEGMENTS.RECIPIENTS);
1505
1544
  const Broadcasts = (transport) => ({
1506
1545
  preview: (body, options = {}) => transport.request(PATHS.BROADCASTS_PREVIEW, {
1507
1546
  method: HTTP_METHODS.POST,
@@ -1523,6 +1562,21 @@ const Broadcasts = (transport) => ({
1523
1562
  listAll: (options = {}) => CollectAll(transport, PATHS.BROADCASTS, CURSOR_STYLES.CURSOR, options, ListQuery$a(options)),
1524
1563
  iterate: (options = {}) => IteratePages(transport, PATHS.BROADCASTS, CURSOR_STYLES.CURSOR, options, ListQuery$a(options)),
1525
1564
  get: (id, options = {}) => transport.request(BroadcastPath(id), { signal: options.signal, apiKey: options.apiKey }),
1565
+ listRecipients: (id, options = {}) => FetchPage(transport, RecipientsPath(id), CURSOR_STYLES.CURSOR, options, RecipientQuery(options)),
1566
+ listAllRecipients: (id, options = {}) => CollectAll(transport, RecipientsPath(id), CURSOR_STYLES.CURSOR, options, RecipientQuery(options)),
1567
+ iterateRecipients: (id, options = {}) => IteratePages(transport, RecipientsPath(id), CURSOR_STYLES.CURSOR, options, RecipientQuery(options)),
1568
+ getRecipient: (id, emailId, options = {}) => transport.request(BroadcastRecipientPath(id, emailId), {
1569
+ signal: options.signal,
1570
+ apiKey: options.apiKey
1571
+ }),
1572
+ stats: (id, options = {}) => transport.request(BroadcastChildPath(id, SEGMENTS.STATS), {
1573
+ query: {
1574
+ [QUERY_KEYS.GRAIN]: options.grain,
1575
+ [QUERY_KEYS.OFFSET_MINUTES]: options.offsetMinutes
1576
+ },
1577
+ signal: options.signal,
1578
+ apiKey: options.apiKey
1579
+ }),
1526
1580
  cancel: (id, options = {}) => transport.request(BroadcastChildPath(id, SEGMENTS.CANCEL), {
1527
1581
  method: HTTP_METHODS.POST,
1528
1582
  repeatable: true,
@@ -1771,13 +1825,34 @@ const Emails = (transport) => ({
1771
1825
  const ListQuery$6 = (options) => ({
1772
1826
  [QUERY_KEYS.Q]: options.q,
1773
1827
  [QUERY_KEYS.KIND]: options.kind,
1828
+ [QUERY_KEYS.DIRECTION]: options.direction,
1829
+ [QUERY_KEYS.ADDRESS]: options.address,
1830
+ [QUERY_KEYS.SINCE]: ToInstant(options.since),
1831
+ [QUERY_KEYS.UNTIL]: ToInstant(options.until),
1774
1832
  [QUERY_KEYS.SORT]: options.sort
1775
1833
  });
1834
+ const UploadName = (options) => {
1835
+ const filename = typeof options?.filename === 'string' ? options.filename.trim() : '';
1836
+ if (!filename)
1837
+ throw new Error(CLIENT_MESSAGES.FILENAME_REQUIRED);
1838
+ return filename;
1839
+ };
1840
+ const UploadType = (data, options) => {
1841
+ if (options.contentType)
1842
+ return options.contentType;
1843
+ return typeof Blob !== 'undefined' && data instanceof Blob && data.type ? data.type : undefined;
1844
+ };
1845
+ const UploadTimeout = (transport, options) => {
1846
+ if (options.timeoutMs !== undefined)
1847
+ return options.timeoutMs;
1848
+ return transport.timeoutMs > 0 ? Math.max(transport.timeoutMs, DEFAULTS.UPLOAD_TIMEOUT_MS) : transport.timeoutMs;
1849
+ };
1776
1850
  const Files = (transport) => ({
1777
1851
  list: (options = {}) => FetchPage(transport, PATHS.FILES, CURSOR_STYLES.CURSOR, options, ListQuery$6(options)),
1778
1852
  listAll: (options = {}) => CollectAll(transport, PATHS.FILES, CURSOR_STYLES.CURSOR, options, ListQuery$6(options)),
1779
1853
  iterate: (options = {}) => IteratePages(transport, PATHS.FILES, CURSOR_STYLES.CURSOR, options, ListQuery$6(options)),
1780
1854
  get: (id, options = {}) => transport.request(FilePath(id), { signal: options.signal, apiKey: options.apiKey }),
1855
+ stats: (options = {}) => transport.request(PATHS.FILES_STATS, { signal: options.signal, apiKey: options.apiKey }),
1781
1856
  download: (id, options = {}) => transport.request(FileChildPath(id, SEGMENTS.CONTENT), {
1782
1857
  binary: true,
1783
1858
  signal: options.signal,
@@ -1786,10 +1861,37 @@ const Files = (transport) => ({
1786
1861
  listLinks: (id, options = {}) => FetchPage(transport, FileChildPath(id, SEGMENTS.LINKS), CURSOR_STYLES.CURSOR, options),
1787
1862
  listAllLinks: (id, options = {}) => CollectAll(transport, FileChildPath(id, SEGMENTS.LINKS), CURSOR_STYLES.CURSOR, options),
1788
1863
  iterateLinks: (id, options = {}) => IteratePages(transport, FileChildPath(id, SEGMENTS.LINKS), CURSOR_STYLES.CURSOR, options),
1864
+ createLink: (id, options = {}) => transport.request(FileChildPath(id, SEGMENTS.LINKS), {
1865
+ method: HTTP_METHODS.POST,
1866
+ body: options.domain ? { domain: options.domain } : {},
1867
+ signal: options.signal,
1868
+ apiKey: options.apiKey
1869
+ }),
1870
+ revokeLink: (id, linkId, options = {}) => transport.request(FileLinkPath(id, linkId), {
1871
+ method: HTTP_METHODS.DELETE,
1872
+ repeatable: true,
1873
+ signal: options.signal,
1874
+ apiKey: options.apiKey
1875
+ }),
1876
+ upload: (data, options) => transport.request(PATHS.FILES, {
1877
+ method: HTTP_METHODS.POST,
1878
+ query: { [QUERY_KEYS.FILENAME]: UploadName(options) },
1879
+ raw: data,
1880
+ contentType: UploadType(data, options),
1881
+ timeoutMs: UploadTimeout(transport, options),
1882
+ signal: options.signal,
1883
+ apiKey: options.apiKey
1884
+ }),
1789
1885
  delete: (id, options = {}) => transport.request(FilePath(id), {
1790
1886
  method: HTTP_METHODS.DELETE,
1791
1887
  signal: options.signal,
1792
1888
  apiKey: options.apiKey
1889
+ }),
1890
+ deleteMany: (ids, options = {}) => transport.request(PATHS.FILES_BATCH_DELETE, {
1891
+ method: HTTP_METHODS.POST,
1892
+ body: { ids },
1893
+ signal: options.signal,
1894
+ apiKey: options.apiKey
1793
1895
  })
1794
1896
  });
1795
1897
 
@@ -2749,6 +2851,7 @@ const languageByCode = LanguageByCode;
2749
2851
  const isRtlLanguage = IsRtlLanguage;
2750
2852
 
2751
2853
  exports.API_SCOPES = API_SCOPES;
2854
+ exports.BROADCAST_RECIPIENT_FILTERS = BROADCAST_RECIPIENT_FILTERS;
2752
2855
  exports.BROADCAST_STATUSES = BROADCAST_STATUSES;
2753
2856
  exports.CONTACT_BLOCK_LISTS = CONTACT_BLOCK_LISTS;
2754
2857
  exports.CONTACT_PHOTO_TYPES = CONTACT_PHOTO_TYPES;
@@ -2757,6 +2860,7 @@ exports.ERROR_TYPES = ERROR_TYPES;
2757
2860
  exports.FILE_DIRECTIONS = FILE_DIRECTIONS;
2758
2861
  exports.FILE_KINDS = FILE_KINDS;
2759
2862
  exports.FILE_SORTS = FILE_SORTS;
2863
+ exports.FILE_USAGES = FILE_USAGES;
2760
2864
  exports.LANGUAGES = LANGUAGES;
2761
2865
  exports.MESSAGE_ENCRYPTION_FORMATS = MESSAGE_ENCRYPTION_FORMATS;
2762
2866
  exports.OpenEmail = OpenEmail;