@internxt/sdk 1.17.3 → 1.17.5

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.
@@ -1,5 +1,6 @@
1
1
  import { ApiSecurity, ApiUrl, AppDetails } from '../shared';
2
- import { MailboxResponse, EmailListResponse, EmailResponse, EmailCreatedResponse, SendEmailRequest, DraftEmailRequest, UpdateEmailRequest, ListEmailsQuery, EmailDomainsResponse, SetupMailAccountPayload, SearchFiltersQuery, MailAccountKeysResponse, MailAccountResponse, LookupRecipientKeysResponse, EncryptedKeystore, KeystoreType, RecipientWithPublicKey, HybridEncryptedEmail, EmailPublicParameters, PwdProtectedEmail } from './types';
2
+ import { RequestCanceler } from '../shared/http/client';
3
+ import { MailboxResponse, EmailListResponse, EmailResponse, EmailCreatedResponse, SendEmailRequest, DraftEmailRequest, UpdateEmailRequest, ListEmailsQuery, EmailDomainsResponse, SetupMailAccountPayload, SearchFiltersQuery, MailAccountKeysResponse, MailAccountResponse, LookupRecipientKeysResponse, EncryptedKeystore, KeystoreType, RecipientWithPublicKey, HybridEncryptedEmail, EmailPublicParameters, PwdProtectedEmail, UploadAttachmentResponse, DownloadAttachmentResponse, DownloadAttachmentPayload } from './types';
3
4
  export declare class MailApi {
4
5
  private readonly client;
5
6
  private readonly appDetails;
@@ -133,6 +134,32 @@ export declare class MailApi {
133
134
  * @returns The public, encrypted private and recovery keys plus the salt
134
135
  */
135
136
  getMailAccountKeys(address?: string): Promise<MailAccountKeysResponse>;
137
+ /**
138
+ * Uploading an attachment to the S3 so we can attach it to an email
139
+ * @param file - File to upload
140
+ * @returns
141
+ * - `blobId` - The blob id of the attachment
142
+ * - `name` - The name of the attachment
143
+ * - `type` - The content type of the attachment
144
+ * - `size` - The size of the attachment
145
+ *
146
+ */
147
+ uploadAttachment(file: File): {
148
+ promise: Promise<UploadAttachmentResponse>;
149
+ requestCanceler: RequestCanceler;
150
+ };
151
+ /**
152
+ * Downloads an attachment of the given email as raw bytes, together with the
153
+ * metadata exposed by the response headers (filename, content type and
154
+ * content length). The caller decides how to consume the bytes (write them
155
+ * to disk, wrap them in a `Blob`, etc.).
156
+ *
157
+ * @param id - The id of the email that owns the attachment
158
+ * @param blobId - The blob id of the attachment
159
+ * @param query - Optional `name` and `type` overrides forwarded to the backend
160
+ * @returns The attachment bytes plus filename, content type and length
161
+ */
162
+ downloadAttachment(id: string, blobId: string, query?: DownloadAttachmentPayload): Promise<DownloadAttachmentResponse>;
136
163
  /**
137
164
  * Returns the needed headers for the module requests
138
165
  * @private
@@ -177,6 +177,43 @@ class MailApi {
177
177
  const params = address ? { address } : {};
178
178
  return this.client.getWithParams('/users/me/mail-account/keys', params, this.headers());
179
179
  }
180
+ /**
181
+ * Uploading an attachment to the S3 so we can attach it to an email
182
+ * @param file - File to upload
183
+ * @returns
184
+ * - `blobId` - The blob id of the attachment
185
+ * - `name` - The name of the attachment
186
+ * - `type` - The content type of the attachment
187
+ * - `size` - The size of the attachment
188
+ *
189
+ */
190
+ uploadAttachment(file) {
191
+ const formData = new FormData();
192
+ formData.append('attachments', file, file.name);
193
+ return this.client.postFormCancellable('/email/attachment', formData, this.headers());
194
+ }
195
+ /**
196
+ * Downloads an attachment of the given email as raw bytes, together with the
197
+ * metadata exposed by the response headers (filename, content type and
198
+ * content length). The caller decides how to consume the bytes (write them
199
+ * to disk, wrap them in a `Blob`, etc.).
200
+ *
201
+ * @param id - The id of the email that owns the attachment
202
+ * @param blobId - The blob id of the attachment
203
+ * @param query - Optional `name` and `type` overrides forwarded to the backend
204
+ * @returns The attachment bytes plus filename, content type and length
205
+ */
206
+ async downloadAttachment(id, blobId, query = {}) {
207
+ const { data, headers } = await this.client.getBinary(`/email/${id}/attachment/${blobId}`, query, this.headers());
208
+ const contentLengthHeader = headers['content-length'];
209
+ const contentLength = contentLengthHeader ? Number(contentLengthHeader) : undefined;
210
+ return {
211
+ data,
212
+ contentType: headers['content-type'] ?? 'application/octet-stream',
213
+ contentLength: Number.isFinite(contentLength) ? contentLength : undefined,
214
+ fileName: parseContentDispositionFilename(headers['content-disposition']),
215
+ };
216
+ }
180
217
  /**
181
218
  * Returns the needed headers for the module requests
182
219
  * @private
@@ -192,3 +229,18 @@ class MailApi {
192
229
  }
193
230
  }
194
231
  exports.MailApi = MailApi;
232
+ function parseContentDispositionFilename(header) {
233
+ if (!header)
234
+ return undefined;
235
+ const utf8Match = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/i.exec(header);
236
+ if (utf8Match) {
237
+ try {
238
+ return decodeURIComponent(utf8Match[1].trim());
239
+ }
240
+ catch {
241
+ return utf8Match[1].trim();
242
+ }
243
+ }
244
+ const asciiMatch = /filename\s*=\s*"?([^";]+)"?/i.exec(header);
245
+ return asciiMatch ? asciiMatch[1].trim() : undefined;
246
+ }
@@ -187,6 +187,46 @@ export interface paths {
187
187
  patch?: never;
188
188
  trace?: never;
189
189
  };
190
+ '/email/attachment': {
191
+ parameters: {
192
+ query?: never;
193
+ header?: never;
194
+ path?: never;
195
+ cookie?: never;
196
+ };
197
+ get?: never;
198
+ put?: never;
199
+ /**
200
+ * Upload an attachment
201
+ * @description Uploads an attachment and get the info to attach it to an user email.
202
+ */
203
+ post: operations['EmailController_uploadAttachment'];
204
+ delete?: never;
205
+ options?: never;
206
+ head?: never;
207
+ patch?: never;
208
+ trace?: never;
209
+ };
210
+ '/email/{id}/attachment/{blobId}': {
211
+ parameters: {
212
+ query?: never;
213
+ header?: never;
214
+ path?: never;
215
+ cookie?: never;
216
+ };
217
+ /**
218
+ * Download an attachment
219
+ * @description Streams the bytes of an attachment from the given email. Optional `name` and `type` query params set the response filename and content-type.
220
+ */
221
+ get: operations['EmailController_downloadAttachment'];
222
+ put?: never;
223
+ post?: never;
224
+ delete?: never;
225
+ options?: never;
226
+ head?: never;
227
+ patch?: never;
228
+ trace?: never;
229
+ };
190
230
  '/users/me/mail-account': {
191
231
  parameters: {
192
232
  query?: never;
@@ -346,6 +386,8 @@ export interface components {
346
386
  encryptedPreview: string;
347
387
  /** @description De-identified wrapped keys; the client trial-decrypts to read */
348
388
  wrappedKeys: components['schemas']['EncryptedWrappedKeyDto'][];
389
+ /** @description De-identified wrapped keys for the symmetric key that encrypts the email's attachments. Present only when the email has encrypted attachments. */
390
+ attachmentWrappedKeys?: components['schemas']['EncryptedWrappedKeyDto'][];
349
391
  };
350
392
  EmailSummaryResponseDto: {
351
393
  /** @example Ma1f09b… */
@@ -428,6 +470,19 @@ export interface components {
428
470
  /** @description Filter by attachment presence */
429
471
  hasAttachment?: boolean;
430
472
  };
473
+ EmailAttachmentDto: {
474
+ /** @example T1a2b3c… */
475
+ blobId: string;
476
+ /** @example photo.jpg */
477
+ name: string;
478
+ /** @example image/jpeg */
479
+ type: string;
480
+ /**
481
+ * @description Size in bytes
482
+ * @example 4096
483
+ */
484
+ size: number;
485
+ };
431
486
  EmailResponseDto: {
432
487
  /** @example Ma1f09b… */
433
488
  id: string;
@@ -470,6 +525,7 @@ export interface components {
470
525
  textBody: string | null;
471
526
  /** @example <p>Hi team, here are the notes…</p> */
472
527
  htmlBody: string | null;
528
+ attachments: components['schemas']['EmailAttachmentDto'][];
473
529
  };
474
530
  LookupRecipientKeysRequestDto: {
475
531
  /**
@@ -499,6 +555,21 @@ export interface components {
499
555
  encryptedText: string;
500
556
  /** @description De-identified wrapped keys, one per recipient */
501
557
  wrappedKeys: components['schemas']['EncryptedWrappedKeyDto'][];
558
+ /** @description De-identified attachment wrapped keys, one per recipient */
559
+ attachmentWrappedKeys: components['schemas']['EncryptedWrappedKeyDto'][];
560
+ };
561
+ AttachmentRefDto: {
562
+ /** @example T1a2b3c… */
563
+ blobId: string;
564
+ /** @example photo.jpg */
565
+ name: string;
566
+ /** @example image/jpeg */
567
+ type: string;
568
+ /**
569
+ * @description Size in bytes
570
+ * @example 4096
571
+ */
572
+ size: number;
502
573
  };
503
574
  SendEmailRequestDto: {
504
575
  /** @description Primary recipients (at least one required) */
@@ -518,6 +589,7 @@ export interface components {
518
589
  */
519
590
  htmlBody?: string;
520
591
  encryption?: components['schemas']['EncryptionBlockDto'];
592
+ attachments?: components['schemas']['AttachmentRefDto'][];
521
593
  };
522
594
  EmailCreatedResponseDto: {
523
595
  /**
@@ -536,6 +608,20 @@ export interface components {
536
608
  textBody?: string;
537
609
  /** @example <p>Still working on this…</p> */
538
610
  htmlBody?: string;
611
+ attachments?: components['schemas']['AttachmentRefDto'][];
612
+ };
613
+ UploadAttachmentResponseDto: {
614
+ /** @example T1a2b3c… */
615
+ blobId: string;
616
+ /**
617
+ * @description Size in bytes
618
+ * @example 4096
619
+ */
620
+ size: number;
621
+ /** @example image/jpeg */
622
+ type: string;
623
+ /** @example photo.jpg */
624
+ name: string;
539
625
  };
540
626
  UpdateEmailRequestDto: {
541
627
  /**
@@ -897,6 +983,51 @@ export interface operations {
897
983
  };
898
984
  };
899
985
  };
986
+ EmailController_uploadAttachment: {
987
+ parameters: {
988
+ query?: never;
989
+ header?: never;
990
+ path?: never;
991
+ cookie?: never;
992
+ };
993
+ requestBody?: never;
994
+ responses: {
995
+ /** @description Upload attachment successfully */
996
+ 200: {
997
+ headers: {
998
+ [name: string]: unknown;
999
+ };
1000
+ content: {
1001
+ 'application/json': components['schemas']['UploadAttachmentResponseDto'];
1002
+ };
1003
+ };
1004
+ };
1005
+ };
1006
+ EmailController_downloadAttachment: {
1007
+ parameters: {
1008
+ query?: {
1009
+ type?: unknown;
1010
+ name?: unknown;
1011
+ };
1012
+ header?: never;
1013
+ path: {
1014
+ /** @description Email ID */
1015
+ id: string;
1016
+ /** @description Attachment blob ID */
1017
+ blobId: string;
1018
+ };
1019
+ cookie?: never;
1020
+ };
1021
+ requestBody?: never;
1022
+ responses: {
1023
+ 200: {
1024
+ headers: {
1025
+ [name: string]: unknown;
1026
+ };
1027
+ content?: never;
1028
+ };
1029
+ };
1030
+ };
900
1031
  UserController_getMailAccount: {
901
1032
  parameters: {
902
1033
  query?: never;
@@ -16,6 +16,15 @@ export type EmailAddress = components['schemas']['EmailAddressDto'];
16
16
  export type ListEmailsQuery = operations['EmailController_list']['parameters']['query'];
17
17
  export type SearchFiltersQuery = operations['EmailController_search']['requestBody']['content']['application/json'];
18
18
  export type EmailDomainsResponse = components['schemas']['MailDomainDto'][];
19
+ export type UploadAttachmentResponse = components['schemas']['UploadAttachmentResponseDto'];
20
+ export type DownloadAttachmentPayload = operations['EmailController_downloadAttachment']['parameters']['query'];
21
+ export type AttachmentRef = components['schemas']['EmailAttachmentDto'];
22
+ export type DownloadAttachmentResponse = {
23
+ data: ArrayBuffer;
24
+ contentType: string;
25
+ contentLength?: number;
26
+ fileName?: string;
27
+ };
19
28
  export type SetupMailAccountPayload = {
20
29
  address: string;
21
30
  domain: string;
@@ -47,6 +47,21 @@ export declare class HttpClient {
47
47
  * @param headers
48
48
  */
49
49
  getWithParams<Response>(url: URL, params: Parameters, headers: Headers): Promise<Response>;
50
+ /**
51
+ * Requests a GET that returns raw binary bytes together with the response
52
+ * headers. Useful for endpoints that stream files where the caller needs
53
+ * `Content-Type`, `Content-Length` or `Content-Disposition`.
54
+ *
55
+ * Bypasses the response interceptor so headers are preserved.
56
+ *
57
+ * @param url
58
+ * @param params
59
+ * @param headers
60
+ */
61
+ getBinary(url: URL, params: Parameters, headers: Headers): Promise<{
62
+ data: ArrayBuffer;
63
+ headers: Record<string, string>;
64
+ }>;
50
65
  /**
51
66
  * Requests a GET with option to cancel
52
67
  * @param url
@@ -77,6 +92,16 @@ export declare class HttpClient {
77
92
  * @param headers
78
93
  */
79
94
  postForm<Response>(url: URL, params: Parameters, headers: Headers): Promise<Response>;
95
+ /**
96
+ * Requests a POST FORM with option to cancel
97
+ * @param url
98
+ * @param params
99
+ * @param headers
100
+ */
101
+ postFormCancellable<Response>(url: URL, params: Parameters, headers: Headers): {
102
+ promise: Promise<Response>;
103
+ requestCanceler: RequestCanceler;
104
+ };
80
105
  /**
81
106
  * Requests a POST with option to cancel
82
107
  * @param url
@@ -72,6 +72,38 @@ class HttpClient {
72
72
  async getWithParams(url, params, headers) {
73
73
  return await this.execute(() => this.axios.get(url, { params, headers }));
74
74
  }
75
+ /**
76
+ * Requests a GET that returns raw binary bytes together with the response
77
+ * headers. Useful for endpoints that stream files where the caller needs
78
+ * `Content-Type`, `Content-Length` or `Content-Disposition`.
79
+ *
80
+ * Bypasses the response interceptor so headers are preserved.
81
+ *
82
+ * @param url
83
+ * @param params
84
+ * @param headers
85
+ */
86
+ async getBinary(url, params, headers) {
87
+ return await this.execute(async () => {
88
+ try {
89
+ const response = await axios_1.default.get(url, {
90
+ baseURL: this.axios.defaults.baseURL,
91
+ params,
92
+ headers,
93
+ responseType: 'arraybuffer',
94
+ transformResponse: (raw) => raw,
95
+ });
96
+ return {
97
+ data: response.data,
98
+ headers: response.headers,
99
+ };
100
+ }
101
+ catch (error) {
102
+ this.normalizeError(error);
103
+ throw error;
104
+ }
105
+ });
106
+ }
75
107
  /**
76
108
  * Requests a GET with option to cancel
77
109
  * @param url
@@ -120,6 +152,22 @@ class HttpClient {
120
152
  async postForm(url, params, headers) {
121
153
  return await this.execute(() => this.axios.postForm(url, params, { headers }));
122
154
  }
155
+ /**
156
+ * Requests a POST FORM with option to cancel
157
+ * @param url
158
+ * @param params
159
+ * @param headers
160
+ */
161
+ postFormCancellable(url, params, headers) {
162
+ let currentCancel = () => { };
163
+ const requestCanceler = { cancel: (message) => currentCancel(message) };
164
+ const promise = this.execute(() => {
165
+ const source = axios_1.default.CancelToken.source();
166
+ currentCancel = source.cancel;
167
+ return this.axios.postForm(url, params, { headers, cancelToken: source.token });
168
+ });
169
+ return { promise, requestCanceler };
170
+ }
123
171
  /**
124
172
  * Requests a POST with option to cancel
125
173
  * @param url
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@internxt/sdk",
3
3
  "author": "Internxt <hello@internxt.com>",
4
- "version": "1.17.3",
4
+ "version": "1.17.5",
5
5
  "description": "An sdk for interacting with Internxt's services",
6
6
  "repository": {
7
7
  "type": "git",