@hasna/connectors 1.3.22 → 1.3.23

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 (63) hide show
  1. package/bin/index.js +109 -59
  2. package/bin/mcp.js +236 -182
  3. package/bin/serve.js +77 -33
  4. package/connectors/connect-dropbox/src/api/bulk.ts +285 -0
  5. package/connectors/connect-dropbox/src/api/index.ts +4 -0
  6. package/connectors/connect-dropbox/src/cli/index.ts +151 -0
  7. package/connectors/connect-github/src/api/bulk.ts +328 -0
  8. package/connectors/connect-github/src/api/index.ts +4 -0
  9. package/connectors/connect-github/src/cli/index.ts +145 -0
  10. package/connectors/connect-gmail/src/api/history.ts +82 -0
  11. package/connectors/connect-gmail/src/api/index.ts +12 -0
  12. package/connectors/connect-gmail/src/api/settings.ts +280 -0
  13. package/connectors/connect-gmail/src/api/watch.ts +40 -0
  14. package/connectors/connect-gmail/src/cli/index.ts +237 -0
  15. package/connectors/connect-googleads/src/api/bulk.ts +354 -0
  16. package/connectors/connect-googleads/src/api/index.ts +4 -0
  17. package/connectors/connect-googleads/src/cli/index.ts +192 -0
  18. package/connectors/connect-googlecalendar/src/api/acl.ts +66 -0
  19. package/connectors/connect-googlecalendar/src/api/bulk.ts +452 -0
  20. package/connectors/connect-googlecalendar/src/api/freebusy.ts +35 -0
  21. package/connectors/connect-googlecalendar/src/api/index.ts +9 -0
  22. package/connectors/connect-googlecalendar/src/cli/index.ts +522 -0
  23. package/connectors/connect-googlecalendar/src/types/index.ts +90 -0
  24. package/connectors/connect-googlecontacts/src/api/bulk.ts +365 -0
  25. package/connectors/connect-googlecontacts/src/api/groups.ts +164 -0
  26. package/connectors/connect-googlecontacts/src/api/index.ts +8 -0
  27. package/connectors/connect-googlecontacts/src/cli/index.ts +340 -0
  28. package/connectors/connect-googledocs/src/api/bulk.ts +301 -0
  29. package/connectors/connect-googledocs/src/api/index.ts +4 -0
  30. package/connectors/connect-googledocs/src/cli/index.ts +187 -0
  31. package/connectors/connect-googledrive/src/api/bulk.ts +505 -0
  32. package/connectors/connect-googledrive/src/api/changes.ts +139 -0
  33. package/connectors/connect-googledrive/src/api/client.ts +34 -0
  34. package/connectors/connect-googledrive/src/api/index.ts +12 -0
  35. package/connectors/connect-googledrive/src/api/revisions.ts +132 -0
  36. package/connectors/connect-googledrive/src/cli/index.ts +624 -0
  37. package/connectors/connect-googledrive/src/index.ts +2 -0
  38. package/connectors/connect-googlephotos/src/api/bulk.ts +455 -0
  39. package/connectors/connect-googlephotos/src/api/index.ts +8 -0
  40. package/connectors/connect-googlephotos/src/cli/index.ts +185 -0
  41. package/connectors/connect-googletasks/src/api/bulk.ts +359 -0
  42. package/connectors/connect-googletasks/src/api/index.ts +1 -0
  43. package/connectors/connect-googletasks/src/cli/index.ts +178 -0
  44. package/connectors/connect-linear/src/api/bulk.ts +219 -0
  45. package/connectors/connect-linear/src/api/index.ts +4 -0
  46. package/connectors/connect-linear/src/cli/index.ts +116 -0
  47. package/connectors/connect-shopify/src/api/bulk.ts +288 -0
  48. package/connectors/connect-shopify/src/api/index.ts +4 -0
  49. package/connectors/connect-shopify/src/cli/index.ts +108 -0
  50. package/connectors/connect-slack/src/api/bulk.ts +237 -0
  51. package/connectors/connect-slack/src/api/index.ts +4 -0
  52. package/connectors/connect-slack/src/cli/index.ts +157 -0
  53. package/connectors/connect-stripe/src/api/bulk.ts +488 -0
  54. package/connectors/connect-stripe/src/api/index.ts +4 -0
  55. package/connectors/connect-stripe/src/cli/index.ts +206 -0
  56. package/dist/index.js +75 -33
  57. package/dist/lib/runner.d.ts +15 -5
  58. package/package.json +2 -2
  59. package/connectors/connect-cloudflare/bun.lock +0 -32
  60. package/connectors/connect-gmail/bin/index.js +0 -7027
  61. package/connectors/connect-gmail/dist/cli/index.js +0 -7035
  62. package/connectors/connect-gmail/dist/index.js +0 -2174
  63. package/dist/server/server.test.d.ts +0 -1
@@ -0,0 +1,328 @@
1
+ import type { GitHubClient } from './client';
2
+
3
+ // ============================================
4
+ // Bulk Operation Types
5
+ // ============================================
6
+
7
+ export interface BulkOperationOptions {
8
+ /** Owner of the repository */
9
+ owner: string;
10
+ /** Repository name */
11
+ repo: string;
12
+ /** Maximum concurrent API calls (default: 10) */
13
+ concurrency?: number;
14
+ /** Dry run - don't actually modify */
15
+ dryRun?: boolean;
16
+ /** Progress callback */
17
+ onProgress?: (current: number, total: number, item: unknown) => void;
18
+ /** Error callback */
19
+ onError?: (error: Error, item: unknown) => void;
20
+ }
21
+
22
+ // --- Issue Bulk Operations ---
23
+
24
+ export interface BulkIssueOptions extends BulkOperationOptions {
25
+ issueNumbers: number[];
26
+ /** Action to perform */
27
+ action: 'close' | 'reopen' | 'lock' | 'unlock';
28
+ /** Lock reason (for lock action only) */
29
+ lockReason?: 'off-topic' | 'too_heated' | 'resolved' | 'spam';
30
+ }
31
+
32
+ export interface BulkIssueResult {
33
+ total: number;
34
+ success: number;
35
+ failed: number;
36
+ errors: Array<{ issueNumber: number; error: string }>;
37
+ results: Array<{ issueNumber: number; response: unknown }>;
38
+ }
39
+
40
+ // --- Label Bulk Operations ---
41
+
42
+ export interface BulkLabelOptions extends BulkOperationOptions {
43
+ issueNumber: number;
44
+ /** Labels to add or remove */
45
+ labels: string[];
46
+ /** Action to perform */
47
+ action: 'add' | 'remove';
48
+ }
49
+
50
+ export interface BulkLabelResult {
51
+ total: number;
52
+ success: number;
53
+ failed: number;
54
+ errors: Array<{ label: string; error: string }>;
55
+ results: Array<{ label: string; response: unknown }>;
56
+ }
57
+
58
+ // --- Pull Request Bulk Operations ---
59
+
60
+ export interface BulkPullRequestOptions extends BulkOperationOptions {
61
+ pullNumbers: number[];
62
+ /** Action to perform */
63
+ action: 'merge' | 'close' | 'update_branch';
64
+ /** Merge method (for merge action) */
65
+ mergeMethod?: 'merge' | 'squash' | 'rebase';
66
+ }
67
+
68
+ export interface BulkPullRequestResult {
69
+ total: number;
70
+ success: number;
71
+ failed: number;
72
+ errors: Array<{ pullNumber: number; error: string }>;
73
+ results: Array<{ pullNumber: number; response: unknown }>;
74
+ }
75
+
76
+ // --- Comment Bulk Operations ---
77
+
78
+ export interface BulkCommentOptions extends BulkOperationOptions {
79
+ /** Comment IDs to operate on */
80
+ commentIds: number[];
81
+ /** Action to perform */
82
+ action: 'delete';
83
+ }
84
+
85
+ export interface BulkCommentResult {
86
+ total: number;
87
+ success: number;
88
+ failed: number;
89
+ errors: Array<{ commentId: number; error: string }>;
90
+ results: Array<{ commentId: number; response: unknown }>;
91
+ }
92
+
93
+ // ============================================
94
+ // Bulk Operations API
95
+ // ============================================
96
+
97
+ export class BulkApi {
98
+ private readonly client: GitHubClient;
99
+
100
+ constructor(client: GitHubClient) {
101
+ this.client = client;
102
+ }
103
+
104
+ // ============================================
105
+ // Bulk Issue Operations
106
+ // ============================================
107
+
108
+ async issues(options: BulkIssueOptions): Promise<BulkIssueResult> {
109
+ const { owner, repo, issueNumbers, action, lockReason, concurrency = 10, dryRun = false, onProgress, onError } = options;
110
+
111
+ const result: BulkIssueResult = {
112
+ total: issueNumbers.length,
113
+ success: 0,
114
+ failed: 0,
115
+ errors: [],
116
+ results: [],
117
+ };
118
+
119
+ if (issueNumbers.length === 0) return result;
120
+
121
+ const chunks = this.chunkArray(issueNumbers, concurrency);
122
+
123
+ for (const chunk of chunks) {
124
+ await Promise.all(
125
+ chunk.map(async (issueNumber) => {
126
+ try {
127
+ if (dryRun) {
128
+ result.success++;
129
+ } else {
130
+ let response: unknown;
131
+ if (action === 'close') {
132
+ response = await this.client.patch(`/repos/${owner}/${repo}/issues/${issueNumber}`, { state: 'closed' });
133
+ } else if (action === 'reopen') {
134
+ response = await this.client.patch(`/repos/${owner}/${repo}/issues/${issueNumber}`, { state: 'open' });
135
+ } else if (action === 'lock') {
136
+ await this.client.put(`/repos/${owner}/${repo}/issues/${issueNumber}/lock`, {
137
+ lock_reason: lockReason,
138
+ });
139
+ response = { locked: true };
140
+ } else {
141
+ await this.client.delete(`/repos/${owner}/${repo}/issues/${issueNumber}/lock`);
142
+ response = { locked: false };
143
+ }
144
+ result.success++;
145
+ result.results.push({ issueNumber, response });
146
+ }
147
+
148
+ if (onProgress) onProgress(result.success + result.failed, result.total, issueNumber);
149
+ } catch (err) {
150
+ result.failed++;
151
+ const errorMessage = err instanceof Error ? err.message : String(err);
152
+ result.errors.push({ issueNumber, error: errorMessage });
153
+ if (onError) onError(err instanceof Error ? err : new Error(errorMessage), issueNumber);
154
+ }
155
+ })
156
+ );
157
+ }
158
+
159
+ return result;
160
+ }
161
+
162
+ // ============================================
163
+ // Bulk Label Operations
164
+ // ============================================
165
+
166
+ async labels(options: BulkLabelOptions): Promise<BulkLabelResult> {
167
+ const { owner, repo, issueNumber, labels, action, concurrency = 10, dryRun = false, onProgress, onError } = options;
168
+
169
+ const result: BulkLabelResult = {
170
+ total: labels.length,
171
+ success: 0,
172
+ failed: 0,
173
+ errors: [],
174
+ results: [],
175
+ };
176
+
177
+ if (labels.length === 0) return result;
178
+
179
+ const chunks = this.chunkArray(labels, concurrency);
180
+
181
+ for (const chunk of chunks) {
182
+ await Promise.all(
183
+ chunk.map(async (label) => {
184
+ try {
185
+ if (dryRun) {
186
+ result.success++;
187
+ } else {
188
+ let response: unknown;
189
+ if (action === 'add') {
190
+ await this.client.post(`/repos/${owner}/${repo}/issues/${issueNumber}/labels`, {
191
+ labels: [label],
192
+ });
193
+ response = { added: label };
194
+ } else {
195
+ await this.client.delete(`/repos/${owner}/${repo}/issues/${issueNumber}/labels/${encodeURIComponent(label)}`);
196
+ response = { removed: label };
197
+ }
198
+ result.success++;
199
+ result.results.push({ label, response });
200
+ }
201
+
202
+ if (onProgress) onProgress(result.success + result.failed, result.total, label);
203
+ } catch (err) {
204
+ result.failed++;
205
+ const errorMessage = err instanceof Error ? err.message : String(err);
206
+ result.errors.push({ label, error: errorMessage });
207
+ if (onError) onError(err instanceof Error ? err : new Error(errorMessage), label);
208
+ }
209
+ })
210
+ );
211
+ }
212
+
213
+ return result;
214
+ }
215
+
216
+ // ============================================
217
+ // Bulk Pull Request Operations
218
+ // ============================================
219
+
220
+ async pullRequests(options: BulkPullRequestOptions): Promise<BulkPullRequestResult> {
221
+ const { owner, repo, pullNumbers, action, mergeMethod = 'merge', concurrency = 10, dryRun = false, onProgress, onError } = options;
222
+
223
+ const result: BulkPullRequestResult = {
224
+ total: pullNumbers.length,
225
+ success: 0,
226
+ failed: 0,
227
+ errors: [],
228
+ results: [],
229
+ };
230
+
231
+ if (pullNumbers.length === 0) return result;
232
+
233
+ const chunks = this.chunkArray(pullNumbers, concurrency);
234
+
235
+ for (const chunk of chunks) {
236
+ await Promise.all(
237
+ chunk.map(async (pullNumber) => {
238
+ try {
239
+ if (dryRun) {
240
+ result.success++;
241
+ } else {
242
+ let response: unknown;
243
+ if (action === 'merge') {
244
+ response = await this.client.put(`/repos/${owner}/${repo}/pulls/${pullNumber}/merge`, {
245
+ merge_method: mergeMethod,
246
+ });
247
+ } else if (action === 'close') {
248
+ response = await this.client.patch(`/repos/${owner}/${repo}/pulls/${pullNumber}`, { state: 'closed' });
249
+ } else {
250
+ response = await this.client.put(`/repos/${owner}/${repo}/pulls/${pullNumber}/update-branch`);
251
+ }
252
+ result.success++;
253
+ result.results.push({ pullNumber, response });
254
+ }
255
+
256
+ if (onProgress) onProgress(result.success + result.failed, result.total, pullNumber);
257
+ } catch (err) {
258
+ result.failed++;
259
+ const errorMessage = err instanceof Error ? err.message : String(err);
260
+ result.errors.push({ pullNumber, error: errorMessage });
261
+ if (onError) onError(err instanceof Error ? err : new Error(errorMessage), pullNumber);
262
+ }
263
+ })
264
+ );
265
+ }
266
+
267
+ return result;
268
+ }
269
+
270
+ // ============================================
271
+ // Bulk Comment Operations
272
+ // ============================================
273
+
274
+ async comments(options: BulkCommentOptions): Promise<BulkCommentResult> {
275
+ const { owner, repo, commentIds, action, concurrency = 10, dryRun = false, onProgress, onError } = options;
276
+
277
+ const result: BulkCommentResult = {
278
+ total: commentIds.length,
279
+ success: 0,
280
+ failed: 0,
281
+ errors: [],
282
+ results: [],
283
+ };
284
+
285
+ if (commentIds.length === 0) return result;
286
+
287
+ const chunks = this.chunkArray(commentIds, concurrency);
288
+
289
+ for (const chunk of chunks) {
290
+ await Promise.all(
291
+ chunk.map(async (commentId) => {
292
+ try {
293
+ if (dryRun) {
294
+ result.success++;
295
+ } else {
296
+ if (action === 'delete') {
297
+ await this.client.delete(`/repos/${owner}/${repo}/issues/comments/${commentId}`);
298
+ }
299
+ result.success++;
300
+ result.results.push({ commentId, response: { deleted: commentId } });
301
+ }
302
+
303
+ if (onProgress) onProgress(result.success + result.failed, result.total, commentId);
304
+ } catch (err) {
305
+ result.failed++;
306
+ const errorMessage = err instanceof Error ? err.message : String(err);
307
+ result.errors.push({ commentId, error: errorMessage });
308
+ if (onError) onError(err instanceof Error ? err : new Error(errorMessage), commentId);
309
+ }
310
+ })
311
+ );
312
+ }
313
+
314
+ return result;
315
+ }
316
+
317
+ // ============================================
318
+ // Helper Methods
319
+ // ============================================
320
+
321
+ private chunkArray<T>(array: T[], size: number): T[][] {
322
+ const chunks: T[][] = [];
323
+ for (let i = 0; i < array.length; i += size) {
324
+ chunks.push(array.slice(i, i + size));
325
+ }
326
+ return chunks;
327
+ }
328
+ }
@@ -4,6 +4,7 @@ import { ReposApi } from './repos';
4
4
  import { IssuesApi } from './issues';
5
5
  import { PullsApi } from './pulls';
6
6
  import { UsersApi } from './users';
7
+ import { BulkApi } from './bulk';
7
8
 
8
9
  /**
9
10
  * Main GitHub connector class
@@ -16,6 +17,7 @@ export class GitHub {
16
17
  public readonly issues: IssuesApi;
17
18
  public readonly pulls: PullsApi;
18
19
  public readonly users: UsersApi;
20
+ public readonly bulk: BulkApi;
19
21
 
20
22
  constructor(config: GitHubConfig) {
21
23
  this.client = new GitHubClient(config);
@@ -23,6 +25,7 @@ export class GitHub {
23
25
  this.issues = new IssuesApi(this.client);
24
26
  this.pulls = new PullsApi(this.client);
25
27
  this.users = new UsersApi(this.client);
28
+ this.bulk = new BulkApi(this.client);
26
29
  }
27
30
 
28
31
  /**
@@ -60,3 +63,4 @@ export { ReposApi } from './repos';
60
63
  export { IssuesApi } from './issues';
61
64
  export { PullsApi } from './pulls';
62
65
  export { UsersApi } from './users';
66
+ export { BulkApi } from './bulk';
@@ -830,5 +830,150 @@ userCmd
830
830
  }
831
831
  });
832
832
 
833
+ // ============================================
834
+ // Bulk Operations Commands
835
+ // ============================================
836
+ const bulkCmd = program
837
+ .command('bulk')
838
+ .description('Bulk operations');
839
+
840
+ bulkCmd
841
+ .command('issues')
842
+ .description('Bulk issue operations')
843
+ .requiredOption('--owner <owner>', 'Repository owner')
844
+ .requiredOption('--repo <repo>', 'Repository name')
845
+ .requiredOption('--numbers <numbers>', 'Comma-separated issue numbers')
846
+ .requiredOption('--action <action>', 'Action: close, reopen, lock, unlock')
847
+ .option('--lock-reason <reason>', 'Lock reason: off-topic, too_heated, resolved, spam')
848
+ .option('--concurrency <number>', 'Max concurrent operations', '10')
849
+ .option('--dry-run', 'Preview without executing')
850
+ .action(async function(this: Command, opts) {
851
+ try {
852
+ const client = getClient();
853
+ const result = await client.bulk.issues({
854
+ owner: opts.owner,
855
+ repo: opts.repo,
856
+ issueNumbers: opts.numbers.split(',').map((n: string) => parseInt(n.trim())),
857
+ action: opts.action as 'close' | 'reopen' | 'lock' | 'unlock',
858
+ lockReason: opts.lockReason,
859
+ concurrency: parseInt(opts.concurrency),
860
+ dryRun: opts.dryRun,
861
+ onProgress: (current, total) => info(`Progress: ${current}/${total}`),
862
+ });
863
+ print(result, getFormat(this));
864
+ info(`Success: ${result.success}, Failed: ${result.failed}, Total: ${result.total}`);
865
+ if (result.errors.length > 0) {
866
+ warn('Errors:');
867
+ result.errors.forEach(e => error(` #${e.issueNumber}: ${e.error}`));
868
+ }
869
+ } catch (err) {
870
+ error(String(err));
871
+ process.exit(1);
872
+ }
873
+ });
874
+
875
+ bulkCmd
876
+ .command('labels')
877
+ .description('Bulk label operations')
878
+ .requiredOption('--owner <owner>', 'Repository owner')
879
+ .requiredOption('--repo <repo>', 'Repository name')
880
+ .requiredOption('--issue-number <number>', 'Issue number')
881
+ .requiredOption('--labels <labels>', 'Comma-separated label names')
882
+ .requiredOption('--action <action>', 'Action: add, remove')
883
+ .option('--concurrency <number>', 'Max concurrent operations', '10')
884
+ .option('--dry-run', 'Preview without executing')
885
+ .action(async function(this: Command, opts) {
886
+ try {
887
+ const client = getClient();
888
+ const result = await client.bulk.labels({
889
+ owner: opts.owner,
890
+ repo: opts.repo,
891
+ issueNumber: parseInt(opts.issueNumber),
892
+ labels: opts.labels.split(',').map((l: string) => l.trim()),
893
+ action: opts.action as 'add' | 'remove',
894
+ concurrency: parseInt(opts.concurrency),
895
+ dryRun: opts.dryRun,
896
+ onProgress: (current, total) => info(`Progress: ${current}/${total}`),
897
+ });
898
+ print(result, getFormat(this));
899
+ info(`Success: ${result.success}, Failed: ${result.failed}, Total: ${result.total}`);
900
+ if (result.errors.length > 0) {
901
+ warn('Errors:');
902
+ result.errors.forEach(e => error(` ${e.label}: ${e.error}`));
903
+ }
904
+ } catch (err) {
905
+ error(String(err));
906
+ process.exit(1);
907
+ }
908
+ });
909
+
910
+ bulkCmd
911
+ .command('pull-requests')
912
+ .description('Bulk pull request operations')
913
+ .requiredOption('--owner <owner>', 'Repository owner')
914
+ .requiredOption('--repo <repo>', 'Repository name')
915
+ .requiredOption('--numbers <numbers>', 'Comma-separated PR numbers')
916
+ .requiredOption('--action <action>', 'Action: merge, close, update_branch')
917
+ .option('--merge-method <method>', 'Merge method: merge, squash, rebase', 'merge')
918
+ .option('--concurrency <number>', 'Max concurrent operations', '10')
919
+ .option('--dry-run', 'Preview without executing')
920
+ .action(async function(this: Command, opts) {
921
+ try {
922
+ const client = getClient();
923
+ const result = await client.bulk.pullRequests({
924
+ owner: opts.owner,
925
+ repo: opts.repo,
926
+ pullNumbers: opts.numbers.split(',').map((n: string) => parseInt(n.trim())),
927
+ action: opts.action as 'merge' | 'close' | 'update_branch',
928
+ mergeMethod: opts.mergeMethod as 'merge' | 'squash' | 'rebase',
929
+ concurrency: parseInt(opts.concurrency),
930
+ dryRun: opts.dryRun,
931
+ onProgress: (current, total) => info(`Progress: ${current}/${total}`),
932
+ });
933
+ print(result, getFormat(this));
934
+ info(`Success: ${result.success}, Failed: ${result.failed}, Total: ${result.total}`);
935
+ if (result.errors.length > 0) {
936
+ warn('Errors:');
937
+ result.errors.forEach(e => error(` #${e.pullNumber}: ${e.error}`));
938
+ }
939
+ } catch (err) {
940
+ error(String(err));
941
+ process.exit(1);
942
+ }
943
+ });
944
+
945
+ bulkCmd
946
+ .command('comments')
947
+ .description('Bulk comment operations')
948
+ .requiredOption('--owner <owner>', 'Repository owner')
949
+ .requiredOption('--repo <repo>', 'Repository name')
950
+ .requiredOption('--ids <ids>', 'Comma-separated comment IDs')
951
+ .requiredOption('--action <action>', 'Action: delete')
952
+ .option('--concurrency <number>', 'Max concurrent operations', '10')
953
+ .option('--dry-run', 'Preview without executing')
954
+ .action(async function(this: Command, opts) {
955
+ try {
956
+ const client = getClient();
957
+ const result = await client.bulk.comments({
958
+ owner: opts.owner,
959
+ repo: opts.repo,
960
+ commentIds: opts.ids.split(',').map((n: string) => parseInt(n.trim())),
961
+ action: opts.action as 'delete',
962
+ concurrency: parseInt(opts.concurrency),
963
+ dryRun: opts.dryRun,
964
+ onProgress: (current, total) => info(`Progress: ${current}/${total}`),
965
+ });
966
+ print(result, getFormat(this));
967
+ info(`Success: ${result.success}, Failed: ${result.failed}, Total: ${result.total}`);
968
+ if (result.errors.length > 0) {
969
+ warn('Errors:');
970
+ result.errors.forEach(e => error(` comment ${e.commentId}: ${e.error}`));
971
+ }
972
+ } catch (err) {
973
+ error(String(err));
974
+ process.exit(1);
975
+ }
976
+ });
977
+
833
978
  // Parse and execute
834
979
  program.parse();
@@ -0,0 +1,82 @@
1
+ import type { GmailClient } from './client';
2
+
3
+ // ============================================
4
+ // History API Types
5
+ // ============================================
6
+
7
+ export interface ListHistoryOptions {
8
+ startHistoryId: string;
9
+ historyTypes?: ('messageAdded' | 'messageChanged' | 'messageDeleted' | 'labelAdded' | 'labelRemoved')[];
10
+ labelId?: string;
11
+ maxResults?: number;
12
+ pageToken?: string;
13
+ }
14
+
15
+ export interface HistoryRecord {
16
+ id: string;
17
+ messages?: Array<{
18
+ id: string;
19
+ threadId: string;
20
+ }>;
21
+ messagesAdded?: Array<{
22
+ message: { id: string; threadId: string };
23
+ }>;
24
+ messagesDeleted?: Array<{
25
+ message: { id: string; threadId: string };
26
+ }>;
27
+ labelsAdded?: Array<{
28
+ message: { id: string; threadId: string };
29
+ labelIds: string[];
30
+ }>;
31
+ labelsRemoved?: Array<{
32
+ message: { id: string; threadId: string };
33
+ labelIds: string[];
34
+ }>;
35
+ messageChanged?: Array<{
36
+ message: { id: string; threadId: string };
37
+ }>;
38
+ }
39
+
40
+ export interface ListHistoryResponse {
41
+ history?: HistoryRecord[];
42
+ nextPageToken?: string;
43
+ historyId: string;
44
+ }
45
+
46
+ /**
47
+ * History API module - retrieve mailbox change history
48
+ */
49
+ export class HistoryApi {
50
+ constructor(private readonly client: GmailClient) {}
51
+
52
+ /**
53
+ * List the history of all changes to the user's mailbox
54
+ */
55
+ async list(options: ListHistoryOptions): Promise<ListHistoryResponse> {
56
+ const params: Record<string, string | number> = {
57
+ startHistoryId: options.startHistoryId,
58
+ };
59
+ if (options.historyTypes && options.historyTypes.length > 0) {
60
+ params.historyTypes = options.historyTypes.join(',');
61
+ }
62
+ if (options.labelId) params.labelId = options.labelId;
63
+ if (options.maxResults) params.maxResults = options.maxResults;
64
+ if (options.pageToken) params.pageToken = options.pageToken;
65
+
66
+ return this.client.get<ListHistoryResponse>('/v1/users/me/history', params);
67
+ }
68
+
69
+ /**
70
+ * Iterate over all history records (handles pagination)
71
+ */
72
+ async *listAll(options: Omit<ListHistoryOptions, 'pageToken'>): AsyncGenerator<HistoryRecord> {
73
+ let pageToken: string | undefined;
74
+ do {
75
+ const response = await this.list({ ...options, pageToken });
76
+ for (const record of response.history || []) {
77
+ yield record;
78
+ }
79
+ pageToken = response.nextPageToken;
80
+ } while (pageToken);
81
+ }
82
+ }
@@ -8,6 +8,9 @@ import { FiltersApi } from './filters';
8
8
  import { AttachmentsApi } from './attachments';
9
9
  import { ExportApi } from './export';
10
10
  import { BulkApi } from './bulk';
11
+ import { HistoryApi } from './history';
12
+ import { SettingsApi } from './settings';
13
+ import { WatchApi } from './watch';
11
14
  import { refreshTokens } from '../utils/auth';
12
15
 
13
16
  /** Tokens passed to Gmail.createWithTokens() */
@@ -33,6 +36,9 @@ export class Gmail {
33
36
  public readonly attachments: AttachmentsApi;
34
37
  public readonly export: ExportApi;
35
38
  public readonly bulk: BulkApi;
39
+ public readonly history: HistoryApi;
40
+ public readonly settings: SettingsApi;
41
+ public readonly watch: WatchApi;
36
42
 
37
43
  constructor(client?: GmailClient) {
38
44
  this.client = client ?? new GmailClient();
@@ -45,6 +51,9 @@ export class Gmail {
45
51
  this.attachments = new AttachmentsApi(this.client);
46
52
  this.export = new ExportApi(this.client);
47
53
  this.bulk = new BulkApi(this.client);
54
+ this.history = new HistoryApi(this.client);
55
+ this.settings = new SettingsApi(this.client);
56
+ this.watch = new WatchApi(this.client);
48
57
  }
49
58
 
50
59
  /**
@@ -156,3 +165,6 @@ export { FiltersApi } from './filters';
156
165
  export { AttachmentsApi } from './attachments';
157
166
  export { ExportApi } from './export';
158
167
  export { BulkApi } from './bulk';
168
+ export { HistoryApi } from './history';
169
+ export { SettingsApi } from './settings';
170
+ export { WatchApi } from './watch';