@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.js CHANGED
@@ -6,8 +6,20 @@ const BROADCAST_STATUSES = {
6
6
  CANCELLED: 'cancelled',
7
7
  FAILED: 'failed'
8
8
  };
9
+ const BROADCAST_RECIPIENT_FILTERS = {
10
+ PENDING: 'pending',
11
+ SENT: 'sent',
12
+ DELIVERED: 'delivered',
13
+ OPENED: 'opened',
14
+ NOT_OPENED: 'not_opened',
15
+ CLICKED: 'clicked',
16
+ BOUNCED: 'bounced',
17
+ COMPLAINED: 'complained',
18
+ FAILED: 'failed',
19
+ UNSUBSCRIBED: 'unsubscribed'
20
+ };
9
21
 
10
- var version = "0.0.2";
22
+ var version = "0.0.4";
11
23
 
12
24
  const BUILD_BASE_URL = 'https://api.openemail.uk';
13
25
 
@@ -15,6 +27,7 @@ const DEFAULTS = {
15
27
  BASE_URL: BUILD_BASE_URL,
16
28
  MAX_RETRIES: 2,
17
29
  TIMEOUT_MS: 30_000,
30
+ UPLOAD_TIMEOUT_MS: 600_000,
18
31
  USER_AGENT: `openemail-sdk/${version}`
19
32
  };
20
33
  const BROWSER_REFUSAL = [
@@ -47,6 +60,7 @@ const CLIENT_MESSAGES = {
47
60
  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.',
48
61
  NO_FETCH: 'No global fetch is available. Pass one as { fetch }, or run on Node 20+, Bun, Deno, Cloudflare Workers or a browser.',
49
62
  EMPTY_SEGMENT: 'An id must not be empty.',
63
+ FILENAME_REQUIRED: 'A file name is required. Pass it as { filename }, the name the file is stored and downloaded under.',
50
64
  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.'
51
65
  };
52
66
 
@@ -128,7 +142,14 @@ const FILE_KINDS = {
128
142
  };
129
143
  const FILE_DIRECTIONS = {
130
144
  INBOUND: 'inbound',
131
- OUTBOUND: 'outbound'
145
+ OUTBOUND: 'outbound',
146
+ UPLOADED: 'uploaded'
147
+ };
148
+ const FILE_USAGES = {
149
+ RECEIVED: 'received',
150
+ SENT: 'sent',
151
+ LINKED: 'linked',
152
+ SCHEDULED: 'scheduled'
132
153
  };
133
154
 
134
155
  const HTTP_METHODS = {
@@ -441,6 +462,8 @@ const QUERY_KEYS = {
441
462
  REASSIGN_TO: 'reassignTo',
442
463
  SOURCE: 'source',
443
464
  ADDRESS: 'address',
465
+ DIRECTION: 'direction',
466
+ FILTER: 'filter',
444
467
  SEARCH: 'search',
445
468
  SORT: 'sort',
446
469
  PAGE: 'page',
@@ -462,7 +485,8 @@ const QUERY_KEYS = {
462
485
  KEY_IDS: 'keyIds',
463
486
  SINCE: 'since',
464
487
  UNTIL: 'until',
465
- FAILED_ONLY: 'failedOnly'
488
+ FAILED_ONLY: 'failedOnly',
489
+ FILENAME: 'filename'
466
490
  };
467
491
  const LIST_SEPARATOR = ',';
468
492
 
@@ -513,6 +537,8 @@ const PATHS = {
513
537
  MEMBERS_INVITATIONS: '/members/invitations',
514
538
  SUPPRESSIONS: '/suppressions',
515
539
  FILES: '/files',
540
+ FILES_STATS: '/files/stats',
541
+ FILES_BATCH_DELETE: '/files/batch-delete',
516
542
  TEMP_MAIL_DOMAINS: '/temp-mail/domains',
517
543
  TEMP_MAIL_INBOXES: '/temp-mail/inboxes'
518
544
  };
@@ -551,8 +577,10 @@ const SEGMENTS = {
551
577
  REVOKE: 'revoke',
552
578
  ROTATE: 'rotate',
553
579
  ROTATE_SECRET: 'rotate-secret',
580
+ RECIPIENTS: 'recipients',
554
581
  SEND: 'send',
555
582
  SENDS: 'sends',
583
+ STATS: 'stats',
556
584
  SNOOZE: 'snooze',
557
585
  START: 'start',
558
586
  TEST: 'test',
@@ -627,6 +655,8 @@ const API_SCOPES = {
627
655
  DRAFTS_WRITE: 'drafts:write',
628
656
  THREADS_READ: 'threads:read',
629
657
  THREADS_WRITE: 'threads:write',
658
+ FILES_READ: 'files:read',
659
+ FILES_WRITE: 'files:write',
630
660
  LABELS_READ: 'labels:read',
631
661
  LABELS_WRITE: 'labels:write',
632
662
  CONTACTS_READ: 'contacts:read',
@@ -688,7 +718,9 @@ const WEBHOOK_EVENTS = {
688
718
  DOMAIN_SENDING_CHANGED: 'domain.sending_changed',
689
719
  DOMAIN_DELETED: 'domain.deleted',
690
720
  SUPPRESSION_ADDED: 'suppression.added',
691
- SUPPRESSION_REMOVED: 'suppression.removed'
721
+ SUPPRESSION_REMOVED: 'suppression.removed',
722
+ FILE_UPLOADED: 'file.uploaded',
723
+ FILE_DELETED: 'file.deleted'
692
724
  };
693
725
  const WEBHOOK_REPLAY_ERROR_CODES = {
694
726
  RESOURCE_NOT_FOUND: 'resource_not_found',
@@ -1144,7 +1176,7 @@ class Transport {
1144
1176
  const repeatable = options.repeatable ?? RETRYABLE_METHODS.has(method);
1145
1177
  let attempt = 0;
1146
1178
  for (;;) {
1147
- const outcome = await this.attempt(url, method, headers, body, options.signal, options.binary === true);
1179
+ const outcome = await this.attempt(url, method, headers, body, options.signal, options.binary === true, options.timeoutMs ?? this.timeoutMs);
1148
1180
  if (outcome instanceof OpenEmailNetworkError) {
1149
1181
  if (options.signal?.aborted || !repeatable || attempt >= this.maxRetries)
1150
1182
  throw outcome;
@@ -1191,13 +1223,13 @@ class Transport {
1191
1223
  headers[HEADER_KEYS.IDEMPOTENCY_KEY] = options.idempotencyKey ?? IdempotencyKey();
1192
1224
  return headers;
1193
1225
  }
1194
- async attempt(url, method, headers, body, signal, binary) {
1195
- const deadline = Deadline(this.timeoutMs, signal);
1226
+ async attempt(url, method, headers, body, signal, binary, timeoutMs) {
1227
+ const deadline = Deadline(timeoutMs, signal);
1196
1228
  const Failure = (error, fallback) => {
1197
1229
  if (signal?.aborted)
1198
1230
  return new OpenEmailNetworkError('Request aborted by the caller.', error);
1199
1231
  if (deadline.timedOut())
1200
- return new OpenEmailNetworkError(`Request timed out after ${this.timeoutMs}ms.`, error, true);
1232
+ return new OpenEmailNetworkError(`Request timed out after ${timeoutMs}ms.`, error, true);
1201
1233
  return new OpenEmailNetworkError(`${fallback}: ${Describe(error)}`, error);
1202
1234
  };
1203
1235
  try {
@@ -1304,6 +1336,7 @@ const AudienceContactsChildPath = (id, segment) => `${AudienceContactsPath(id)}/
1304
1336
  const AudienceContactPath = (id, email) => `${AudienceContactsPath(id)}/${Segment(email)}`;
1305
1337
  const BroadcastPath = (id) => `${PATHS.BROADCASTS}/${Segment(id)}`;
1306
1338
  const BroadcastChildPath = (id, segment) => `${BroadcastPath(id)}/${segment}`;
1339
+ const BroadcastRecipientPath = (id, emailId) => `${BroadcastChildPath(id, SEGMENTS.RECIPIENTS)}/${Segment(emailId)}`;
1307
1340
  const DomainPath = (id) => `${PATHS.DOMAINS}/${Segment(id)}`;
1308
1341
  const WebhookPath = (id) => `${PATHS.WEBHOOKS}/${Segment(id)}`;
1309
1342
  const WebhookChildPath = (id, segment) => `${WebhookPath(id)}/${segment}`;
@@ -1336,6 +1369,7 @@ const MemberInvitationResendPath = (id) => `${MemberInvitationPath(id)}/${SEGMEN
1336
1369
  const SuppressionPath = (id) => `${PATHS.SUPPRESSIONS}/${Segment(id)}`;
1337
1370
  const FilePath = (id) => `${PATHS.FILES}/${Segment(id)}`;
1338
1371
  const FileChildPath = (id, segment) => `${FilePath(id)}/${segment}`;
1372
+ const FileLinkPath = (id, linkId) => `${FileChildPath(id, SEGMENTS.LINKS)}/${Segment(linkId)}`;
1339
1373
  const TempInboxPath = (id) => `${PATHS.TEMP_MAIL_INBOXES}/${Segment(id)}`;
1340
1374
  const TempInboxExtendPath = (id) => `${TempInboxPath(id)}/${SEGMENTS.EXTEND}`;
1341
1375
  const TempMessagesPath = (id) => `${TempInboxPath(id)}/${SEGMENTS.MESSAGES}`;
@@ -1498,6 +1532,11 @@ const ToInstant = Instant;
1498
1532
  const ListQuery$a = (options) => ({
1499
1533
  [QUERY_KEYS.AUDIENCE_ID]: options.audienceId
1500
1534
  });
1535
+ const RecipientQuery = (options) => ({
1536
+ [QUERY_KEYS.FILTER]: options.filter,
1537
+ [QUERY_KEYS.Q]: options.q
1538
+ });
1539
+ const RecipientsPath = (id) => BroadcastChildPath(id, SEGMENTS.RECIPIENTS);
1501
1540
  const Broadcasts = (transport) => ({
1502
1541
  preview: (body, options = {}) => transport.request(PATHS.BROADCASTS_PREVIEW, {
1503
1542
  method: HTTP_METHODS.POST,
@@ -1519,6 +1558,21 @@ const Broadcasts = (transport) => ({
1519
1558
  listAll: (options = {}) => CollectAll(transport, PATHS.BROADCASTS, CURSOR_STYLES.CURSOR, options, ListQuery$a(options)),
1520
1559
  iterate: (options = {}) => IteratePages(transport, PATHS.BROADCASTS, CURSOR_STYLES.CURSOR, options, ListQuery$a(options)),
1521
1560
  get: (id, options = {}) => transport.request(BroadcastPath(id), { signal: options.signal, apiKey: options.apiKey }),
1561
+ listRecipients: (id, options = {}) => FetchPage(transport, RecipientsPath(id), CURSOR_STYLES.CURSOR, options, RecipientQuery(options)),
1562
+ listAllRecipients: (id, options = {}) => CollectAll(transport, RecipientsPath(id), CURSOR_STYLES.CURSOR, options, RecipientQuery(options)),
1563
+ iterateRecipients: (id, options = {}) => IteratePages(transport, RecipientsPath(id), CURSOR_STYLES.CURSOR, options, RecipientQuery(options)),
1564
+ getRecipient: (id, emailId, options = {}) => transport.request(BroadcastRecipientPath(id, emailId), {
1565
+ signal: options.signal,
1566
+ apiKey: options.apiKey
1567
+ }),
1568
+ stats: (id, options = {}) => transport.request(BroadcastChildPath(id, SEGMENTS.STATS), {
1569
+ query: {
1570
+ [QUERY_KEYS.GRAIN]: options.grain,
1571
+ [QUERY_KEYS.OFFSET_MINUTES]: options.offsetMinutes
1572
+ },
1573
+ signal: options.signal,
1574
+ apiKey: options.apiKey
1575
+ }),
1522
1576
  cancel: (id, options = {}) => transport.request(BroadcastChildPath(id, SEGMENTS.CANCEL), {
1523
1577
  method: HTTP_METHODS.POST,
1524
1578
  repeatable: true,
@@ -1767,13 +1821,34 @@ const Emails = (transport) => ({
1767
1821
  const ListQuery$6 = (options) => ({
1768
1822
  [QUERY_KEYS.Q]: options.q,
1769
1823
  [QUERY_KEYS.KIND]: options.kind,
1824
+ [QUERY_KEYS.DIRECTION]: options.direction,
1825
+ [QUERY_KEYS.ADDRESS]: options.address,
1826
+ [QUERY_KEYS.SINCE]: ToInstant(options.since),
1827
+ [QUERY_KEYS.UNTIL]: ToInstant(options.until),
1770
1828
  [QUERY_KEYS.SORT]: options.sort
1771
1829
  });
1830
+ const UploadName = (options) => {
1831
+ const filename = typeof options?.filename === 'string' ? options.filename.trim() : '';
1832
+ if (!filename)
1833
+ throw new Error(CLIENT_MESSAGES.FILENAME_REQUIRED);
1834
+ return filename;
1835
+ };
1836
+ const UploadType = (data, options) => {
1837
+ if (options.contentType)
1838
+ return options.contentType;
1839
+ return typeof Blob !== 'undefined' && data instanceof Blob && data.type ? data.type : undefined;
1840
+ };
1841
+ const UploadTimeout = (transport, options) => {
1842
+ if (options.timeoutMs !== undefined)
1843
+ return options.timeoutMs;
1844
+ return transport.timeoutMs > 0 ? Math.max(transport.timeoutMs, DEFAULTS.UPLOAD_TIMEOUT_MS) : transport.timeoutMs;
1845
+ };
1772
1846
  const Files = (transport) => ({
1773
1847
  list: (options = {}) => FetchPage(transport, PATHS.FILES, CURSOR_STYLES.CURSOR, options, ListQuery$6(options)),
1774
1848
  listAll: (options = {}) => CollectAll(transport, PATHS.FILES, CURSOR_STYLES.CURSOR, options, ListQuery$6(options)),
1775
1849
  iterate: (options = {}) => IteratePages(transport, PATHS.FILES, CURSOR_STYLES.CURSOR, options, ListQuery$6(options)),
1776
1850
  get: (id, options = {}) => transport.request(FilePath(id), { signal: options.signal, apiKey: options.apiKey }),
1851
+ stats: (options = {}) => transport.request(PATHS.FILES_STATS, { signal: options.signal, apiKey: options.apiKey }),
1777
1852
  download: (id, options = {}) => transport.request(FileChildPath(id, SEGMENTS.CONTENT), {
1778
1853
  binary: true,
1779
1854
  signal: options.signal,
@@ -1782,10 +1857,37 @@ const Files = (transport) => ({
1782
1857
  listLinks: (id, options = {}) => FetchPage(transport, FileChildPath(id, SEGMENTS.LINKS), CURSOR_STYLES.CURSOR, options),
1783
1858
  listAllLinks: (id, options = {}) => CollectAll(transport, FileChildPath(id, SEGMENTS.LINKS), CURSOR_STYLES.CURSOR, options),
1784
1859
  iterateLinks: (id, options = {}) => IteratePages(transport, FileChildPath(id, SEGMENTS.LINKS), CURSOR_STYLES.CURSOR, options),
1860
+ createLink: (id, options = {}) => transport.request(FileChildPath(id, SEGMENTS.LINKS), {
1861
+ method: HTTP_METHODS.POST,
1862
+ body: options.domain ? { domain: options.domain } : {},
1863
+ signal: options.signal,
1864
+ apiKey: options.apiKey
1865
+ }),
1866
+ revokeLink: (id, linkId, options = {}) => transport.request(FileLinkPath(id, linkId), {
1867
+ method: HTTP_METHODS.DELETE,
1868
+ repeatable: true,
1869
+ signal: options.signal,
1870
+ apiKey: options.apiKey
1871
+ }),
1872
+ upload: (data, options) => transport.request(PATHS.FILES, {
1873
+ method: HTTP_METHODS.POST,
1874
+ query: { [QUERY_KEYS.FILENAME]: UploadName(options) },
1875
+ raw: data,
1876
+ contentType: UploadType(data, options),
1877
+ timeoutMs: UploadTimeout(transport, options),
1878
+ signal: options.signal,
1879
+ apiKey: options.apiKey
1880
+ }),
1785
1881
  delete: (id, options = {}) => transport.request(FilePath(id), {
1786
1882
  method: HTTP_METHODS.DELETE,
1787
1883
  signal: options.signal,
1788
1884
  apiKey: options.apiKey
1885
+ }),
1886
+ deleteMany: (ids, options = {}) => transport.request(PATHS.FILES_BATCH_DELETE, {
1887
+ method: HTTP_METHODS.POST,
1888
+ body: { ids },
1889
+ signal: options.signal,
1890
+ apiKey: options.apiKey
1789
1891
  })
1790
1892
  });
1791
1893
 
@@ -2744,4 +2846,4 @@ const resolveLanguage = ResolveLanguage;
2744
2846
  const languageByCode = LanguageByCode;
2745
2847
  const isRtlLanguage = IsRtlLanguage;
2746
2848
 
2747
- export { API_SCOPES, BROADCAST_STATUSES, CONTACT_BLOCK_LISTS, CONTACT_PHOTO_TYPES, CONTACT_THREAD_SORTS, ERROR_TYPES, FILE_DIRECTIONS, FILE_KINDS, FILE_SORTS, LANGUAGES, MESSAGE_ENCRYPTION_FORMATS, OpenEmail, OpenEmailApiError, OpenEmailNetworkError, PAGE_LIMITS, PEOPLE_SORTS, PROVIDER_IMPORT_DOMAIN_STATES, PROVIDER_IMPORT_RESOURCES, PROVIDER_IMPORT_STATUSES, RULE_ACTIONS, RULE_FIELDS, RULE_OPERATORS, SUPPRESSION_REASONS, THREAD_SORTS, VERSION, WEBHOOK_EVENTS, WEBHOOK_REPLAY_ERROR_CODES, WEBHOOK_SIGNATURE_HEADERS, createClient, createOpenEmail, createTempMail, OpenEmail as default, getClient, init, isApiKey, isRtlLanguage, isSealed, languageByCode, openemail, resetClient, resolveLanguage, toBase64, verifyWebhookSignature };
2849
+ export { API_SCOPES, BROADCAST_RECIPIENT_FILTERS, BROADCAST_STATUSES, CONTACT_BLOCK_LISTS, CONTACT_PHOTO_TYPES, CONTACT_THREAD_SORTS, ERROR_TYPES, FILE_DIRECTIONS, FILE_KINDS, FILE_SORTS, FILE_USAGES, LANGUAGES, MESSAGE_ENCRYPTION_FORMATS, OpenEmail, OpenEmailApiError, OpenEmailNetworkError, PAGE_LIMITS, PEOPLE_SORTS, PROVIDER_IMPORT_DOMAIN_STATES, PROVIDER_IMPORT_RESOURCES, PROVIDER_IMPORT_STATUSES, RULE_ACTIONS, RULE_FIELDS, RULE_OPERATORS, SUPPRESSION_REASONS, THREAD_SORTS, VERSION, WEBHOOK_EVENTS, WEBHOOK_REPLAY_ERROR_CODES, WEBHOOK_SIGNATURE_HEADERS, createClient, createOpenEmail, createTempMail, OpenEmail as default, getClient, init, isApiKey, isRtlLanguage, isSealed, languageByCode, openemail, resetClient, resolveLanguage, toBase64, verifyWebhookSignature };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openemail/sdk",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "author": "OpenEmail",
5
5
  "license": "UNLICENSED",
6
6
  "description": "The official TypeScript SDK for the OpenEmail API. Send email and broadcasts, work with threads, drafts, labels, templates, rules, webhooks, tracking, contacts, audiences, domains, suppressions, files, API keys, roles, members, mailbox imports and disposable inboxes from code.",