@kubun/connector-google-mail 0.17.3 → 0.18.0

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/lib/actions.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ActionHandler, WriteAttachment } from '@kubun/connector';
1
+ import { type ActionHandler, type WriteAttachment } from '@kubun/connector';
2
2
  export type GoogleMailActionHandlerConfig = {
3
3
  credential: {
4
4
  accessToken: string;
package/lib/actions.js CHANGED
@@ -1,14 +1,16 @@
1
+ import { carryField, refuseCutLists } from '@kubun/connector';
1
2
  import { createDraft, createLabel, importMessageAttachment, modifyLabels, sendMessage, updateLabel } from './api.js';
2
3
  import { GoogleMailError } from './errors.js';
3
4
  // Gmail takes a label color only as the textColor/backgroundColor pair, so a
4
5
  // write that changes one of them carries the other from the target document.
5
6
  // With no companion to carry (absent or empty), the color cannot be written at all: the write is
6
- // rejected before any request, rather than sent without the change.
7
- function withColorPair(params, existing) {
7
+ // rejected before any request, rather than sent without the change. A
8
+ // companion trimmed at sync is refused too (`carryField`), never sent.
9
+ function withColorPair(params, existing, context) {
8
10
  const changesOne = 'textColor' in params !== 'backgroundColor' in params;
9
11
  if (!changesOne) return params;
10
12
  const missing = 'textColor' in params ? 'backgroundColor' : 'textColor';
11
- const companion = existing?.[missing];
13
+ const companion = carryField(existing, missing, context);
12
14
  if (typeof companion !== 'string' || companion === '') {
13
15
  throw new GoogleMailError(`Google Mail: a label color needs both textColor and backgroundColor, and the label has no ${missing} to keep`, {
14
16
  kind: 'rejected'
@@ -58,12 +60,13 @@ export function createGoogleMailActionHandler(config) {
58
60
  sourceFetchHLC: mintActionFetchHLC()
59
61
  };
60
62
  },
61
- async update (model, sourceID, params, existing) {
63
+ async update (model, sourceID, params, existing, context) {
64
+ refuseCutLists(params, context);
62
65
  if (model === 'GoogleMailLabel') {
63
66
  const record = await updateLabel({
64
67
  accessToken: config.credential.accessToken,
65
68
  labelID: sourceID,
66
- params: withColorPair(params, existing),
69
+ params: withColorPair(params, existing, context),
67
70
  fetch: config.fetch
68
71
  });
69
72
  return {
package/lib/api.d.ts CHANGED
@@ -103,6 +103,17 @@ export declare function getHeader(headers: Array<{
103
103
  }> | undefined, name: string): string | undefined;
104
104
  export declare function extractEmail(headerValue: string): string;
105
105
  export declare function extractEmails(headerValue: string | undefined): Array<string>;
106
+ export type MailParty = {
107
+ name?: string;
108
+ email?: string;
109
+ };
110
+ /**
111
+ * Parses one mailbox out of a header value: `"Alice A" <alice@example.com>`,
112
+ * `Alice A <alice@example.com>`, or a bare `alice@example.com`. A value that
113
+ * names no address at all -- malformed or empty -- returns `{}` rather than
114
+ * a name with no email, or an email this parser only guessed at.
115
+ */
116
+ export declare function parseMailParty(value: string): MailParty;
106
117
  export declare function mapMessageToEntity(message: GmailMessageResource): EntityRecord;
107
118
  export declare function getMessage(config: {
108
119
  accessToken: string;
package/lib/api.js CHANGED
@@ -78,6 +78,93 @@ export function extractEmails(headerValue) {
78
78
  if (headerValue == null || headerValue.trim() === '') return [];
79
79
  return headerValue.split(',').map((part)=>extractEmail(part));
80
80
  }
81
+ // A mailbox is either `Display Name <addr@example.com>` (the display name
82
+ // optionally quoted) or a bare `addr@example.com`. This is a liberal but
83
+ // bounded check for "looks like an email address" -- no whitespace, angle
84
+ // bracket or quote characters around a single '@', with at least one '.' in
85
+ // the domain -- never a full RFC 5322 grammar. Gmail headers are already
86
+ // provider-validated, so this only guards against a header this parser
87
+ // cannot make sense of; it never invents an address that is not there.
88
+ const MAILBOX_WITH_NAME = /^([^<>]*)<([^<>]*)>\s*$/;
89
+ const PLAUSIBLE_EMAIL = /^[^\s<>"@]+@[^\s<>"@]+\.[^\s<>"@]+$/;
90
+ function stripQuotes(value) {
91
+ const trimmed = value.trim();
92
+ const match = /^"(.*)"$/.exec(trimmed);
93
+ return match?.[1]?.replace(/\\(["\\])/g, '$1') ?? trimmed;
94
+ }
95
+ /**
96
+ * Parses one mailbox out of a header value: `"Alice A" <alice@example.com>`,
97
+ * `Alice A <alice@example.com>`, or a bare `alice@example.com`. A value that
98
+ * names no address at all -- malformed or empty -- returns `{}` rather than
99
+ * a name with no email, or an email this parser only guessed at.
100
+ */ export function parseMailParty(value) {
101
+ const trimmed = value.trim();
102
+ if (trimmed === '') return {};
103
+ const withName = MAILBOX_WITH_NAME.exec(trimmed);
104
+ if (withName != null) {
105
+ const email = (withName[2] ?? '').trim();
106
+ if (!PLAUSIBLE_EMAIL.test(email)) return {};
107
+ const name = stripQuotes(withName[1] ?? '');
108
+ return name === '' ? {
109
+ email
110
+ } : {
111
+ name,
112
+ email
113
+ };
114
+ }
115
+ return PLAUSIBLE_EMAIL.test(trimmed) ? {
116
+ email: trimmed
117
+ } : {};
118
+ }
119
+ // Split only at mailbox boundaries outside quoted names and angle addresses.
120
+ // A group label is not a mailbox; its comma-separated members are.
121
+ function splitMailboxes(headerValue) {
122
+ const parts = [];
123
+ let current = '';
124
+ let inQuotes = false;
125
+ let inAngle = false;
126
+ let inGroup = false;
127
+ const push = ()=>{
128
+ if (current.trim() !== '') parts.push(current.trim());
129
+ current = '';
130
+ };
131
+ for(let i = 0; i < headerValue.length; i++){
132
+ const char = headerValue[i];
133
+ if (char === '\\' && inQuotes && i + 1 < headerValue.length) {
134
+ current += char + headerValue[++i];
135
+ continue;
136
+ }
137
+ if (char === '"' && !inAngle) inQuotes = !inQuotes;
138
+ if (!inQuotes && char === '<') inAngle = true;
139
+ if (!inQuotes && char === '>') inAngle = false;
140
+ if (!inQuotes && !inAngle) {
141
+ if (char === ':' && !inGroup && /^[^@<>:;]+$/.test(current.trim())) {
142
+ current = '';
143
+ inGroup = true;
144
+ continue;
145
+ }
146
+ if (char === ',' || char === ';' && inGroup) {
147
+ push();
148
+ if (char === ';') inGroup = false;
149
+ continue;
150
+ }
151
+ }
152
+ current += char;
153
+ if (char === '>' && !inQuotes) {
154
+ const next = headerValue.slice(i + 1).trimStart();
155
+ if (next.startsWith('<')) push();
156
+ }
157
+ }
158
+ push();
159
+ return parts;
160
+ }
161
+ function parseMailPartyList(headerValue) {
162
+ if (headerValue == null || headerValue.trim() === '') return [];
163
+ return splitMailboxes(headerValue).map(parseMailParty).filter((party)=>party.name != null || party.email != null);
164
+ }
165
+ function nonEmptyPartyList(parties) {
166
+ return parties.length === 0 ? undefined : parties;
167
+ }
81
168
  function findPartByMime(part, mime) {
82
169
  if (part.mimeType === mime && part.body?.data != null) return part;
83
170
  for (const child of part.parts ?? []){
@@ -117,16 +204,19 @@ export function mapMessageToEntity(message) {
117
204
  const headers = message.payload?.headers;
118
205
  const labels = message.labelIds ?? [];
119
206
  const sentAtMs = message.internalDate != null ? Number.parseInt(message.internalDate, 10) : Number.NaN;
207
+ const from = parseMailParty(getHeader(headers, 'From') ?? '');
120
208
  return {
121
209
  sourceService: 'google-mail',
122
210
  sourceID: message.id,
123
211
  sourceURL: `https://mail.google.com/mail/u/0/#inbox/${message.id}`,
124
212
  body: extractBody(message.payload),
125
- sender: extractEmail(getHeader(headers, 'From') ?? ''),
213
+ // A message with no From header, or one this parser cannot make sense
214
+ // of, carries no `from` rather than an empty Party.
215
+ from: from.name != null || from.email != null ? from : undefined,
126
216
  subject: getHeader(headers, 'Subject'),
127
217
  snippet: message.snippet,
128
- to: extractEmails(getHeader(headers, 'To')),
129
- cc: extractEmails(getHeader(headers, 'Cc')),
218
+ to: nonEmptyPartyList(parseMailPartyList(getHeader(headers, 'To'))),
219
+ cc: nonEmptyPartyList(parseMailPartyList(getHeader(headers, 'Cc'))),
130
220
  labels,
131
221
  unread: labels.includes('UNREAD'),
132
222
  sentAt: Number.isNaN(sentAtMs) ? undefined : new Date(sentAtMs).toISOString(),
@@ -262,7 +352,7 @@ function requireString(params, key) {
262
352
  }
263
353
  // Gmail's write endpoints (messages.send, drafts.create, messages.modify) return only
264
354
  // { id, threadId, labelIds } -- no payload/headers/body. Re-fetch the full message so the
265
- // mapped entity carries real sender/body/subject/etc. instead of empty placeholders.
355
+ // mapped entity carries real from/body/subject/etc. instead of empty placeholders.
266
356
  // Everything here runs after the write went out, so any failure is marked sent: the
267
357
  // write may have landed, whatever the read-back did.
268
358
  async function readBackWrittenMessage(config) {
package/lib/model.d.ts CHANGED
@@ -5,10 +5,6 @@ export declare const googleMailThreadDefinition: {
5
5
  readonly schema: {
6
6
  readonly type: "object";
7
7
  readonly properties: {
8
- readonly title: {
9
- readonly type: "string";
10
- readonly maxLength: 200;
11
- };
12
8
  readonly sourceService: {
13
9
  readonly $ref: "#/definitions/SourceService";
14
10
  };
@@ -47,7 +43,7 @@ export declare const googleMailThreadDefinition: {
47
43
  };
48
44
  readonly fieldsMeta: {};
49
45
  readonly providerMergePolicy: {
50
- readonly group: ["title", "sourceService", "sourceID", "sourceURL"];
46
+ readonly group: [...string[], "sourceService", "sourceID", "sourceURL"];
51
47
  };
52
48
  };
53
49
  export declare const googleMailMessageDefinition: {
@@ -57,43 +53,16 @@ export declare const googleMailMessageDefinition: {
57
53
  readonly schema: {
58
54
  readonly type: "object";
59
55
  readonly properties: {
60
- readonly body: {
61
- readonly type: "string";
62
- };
63
- readonly sender: {
64
- readonly type: "string";
65
- readonly minLength: 1;
66
- readonly maxLength: 320;
67
- };
68
- readonly subject: {
69
- readonly type: "string";
70
- };
71
56
  readonly snippet: {
72
57
  readonly type: "string";
73
58
  };
74
- readonly to: {
75
- readonly type: "array";
76
- readonly title: "To";
77
- readonly items: {
78
- readonly type: "string";
79
- };
80
- };
81
- readonly cc: {
82
- readonly type: "array";
83
- readonly title: "Cc";
84
- readonly items: {
85
- readonly type: "string";
86
- };
87
- };
88
59
  readonly labels: {
89
60
  readonly type: "array";
90
61
  readonly title: "Labels";
91
62
  readonly items: {
92
63
  readonly type: "string";
93
64
  };
94
- };
95
- readonly sentAt: {
96
- readonly $ref: "#/definitions/DateTime";
65
+ readonly maxItems: 100;
97
66
  };
98
67
  readonly unread: {
99
68
  readonly type: "boolean";
@@ -104,13 +73,11 @@ export declare const googleMailMessageDefinition: {
104
73
  readonly items: {
105
74
  readonly $ref: "#/definitions/Attachment";
106
75
  };
76
+ readonly maxItems: 100;
107
77
  };
108
78
  readonly threadID: {
109
79
  readonly type: "string";
110
80
  };
111
- readonly thread: {
112
- readonly $ref: "#/definitions/ThreadDocID";
113
- };
114
81
  readonly sourceService: {
115
82
  readonly $ref: "#/definitions/SourceService";
116
83
  };
@@ -124,14 +91,9 @@ export declare const googleMailMessageDefinition: {
124
91
  readonly $ref: "#/definitions/DateTime";
125
92
  };
126
93
  };
127
- readonly required: ["body", "sender", "threadID", "thread", "sourceService", "sourceID", "lastSyncedAt"];
94
+ readonly required: ["threadID", "thread", "sourceService", "sourceID", "lastSyncedAt"];
128
95
  readonly additionalProperties: false;
129
96
  readonly definitions: {
130
- readonly ThreadDocID: {
131
- readonly type: "string";
132
- readonly title: "DocID";
133
- readonly pattern: "^k[0-9a-z]{10,120}$";
134
- };
135
97
  readonly Attachment: {
136
98
  readonly type: "object";
137
99
  readonly title: "Attachment";
@@ -176,14 +138,9 @@ export declare const googleMailMessageDefinition: {
176
138
  readonly DateTime: import("json-schema-typed").JSONSchema.String;
177
139
  };
178
140
  };
179
- readonly fieldsMeta: {
180
- readonly thread: {
181
- readonly type: "document";
182
- readonly model: "#0";
183
- };
184
- };
141
+ readonly fieldsMeta: {};
185
142
  readonly providerMergePolicy: {
186
- readonly group: ["body", "sender", "subject", "snippet", "to", "cc", "labels", "sentAt", "unread", "threadID", "thread", "sourceService", "sourceID", "sourceURL"];
143
+ readonly group: [...string[], "sourceService", "sourceID", "sourceURL"];
187
144
  };
188
145
  };
189
146
  export declare const googleMailLabelDefinition: {
package/lib/model.js CHANGED
@@ -1,21 +1,25 @@
1
1
  import { connectorClusters, connectorModelIDs, externalEntityDefinition } from '@kubun/connector';
2
- import { modelIDs, modelsClusters } from '@kubun/models';
2
+ import { SHORT_LIST_MAX_ITEMS } from '@kubun/models-core';
3
+ import { messagingClusters, messagingModelDefinitions, messagingModelIDs } from '@kubun/models-messaging';
3
4
  import { GraphBuilder } from '@kubun/protocol';
4
- import { docIDSchema } from '@kubun/scalars';
5
+ const threadKindProviderFields = messagingModelDefinitions.find((definition)=>definition.name === 'ThreadKind')?.providerMergePolicy?.group;
6
+ const messageKindProviderFields = messagingModelDefinitions.find((definition)=>definition.name === 'MessageKind')?.providerMergePolicy?.group;
7
+ const emailKindProviderFields = messagingModelDefinitions.find((definition)=>definition.name === 'EmailKind')?.providerMergePolicy?.group;
8
+ if (threadKindProviderFields == null || messageKindProviderFields == null || emailKindProviderFields == null) {
9
+ throw new Error('Messaging provider fields are missing');
10
+ }
11
+ // The shared thread field (title) comes from ThreadKind; only what is
12
+ // Google's own is declared here.
5
13
  export const googleMailThreadDefinition = {
6
14
  name: 'GoogleMailThread',
7
15
  behavior: 'default',
8
16
  interfaces: [
9
17
  connectorModelIDs.ExternalEntity,
10
- modelIDs.ThreadKind
18
+ messagingModelIDs.ThreadKind
11
19
  ],
12
20
  schema: {
13
21
  type: 'object',
14
22
  properties: {
15
- title: {
16
- type: 'string',
17
- maxLength: 200
18
- },
19
23
  sourceService: {
20
24
  $ref: '#/definitions/SourceService'
21
25
  },
@@ -44,60 +48,35 @@ export const googleMailThreadDefinition = {
44
48
  // provider content, so it stays outside the group.
45
49
  providerMergePolicy: {
46
50
  group: [
47
- 'title',
51
+ ...threadKindProviderFields,
48
52
  'sourceService',
49
53
  'sourceID',
50
54
  'sourceURL'
51
55
  ]
52
56
  }
53
57
  };
58
+ // Generic message fields come from MessageKind, and mail fields from EmailKind.
59
+ // Only what is Google's own is declared here.
54
60
  export const googleMailMessageDefinition = {
55
61
  name: 'GoogleMailMessage',
56
62
  behavior: 'default',
57
63
  interfaces: [
58
64
  connectorModelIDs.ExternalEntity,
59
- modelIDs.MessageKind
65
+ messagingModelIDs.EmailKind
60
66
  ],
61
67
  schema: {
62
68
  type: 'object',
63
69
  properties: {
64
- body: {
65
- type: 'string'
66
- },
67
- sender: {
68
- type: 'string',
69
- minLength: 1,
70
- maxLength: 320
71
- },
72
- subject: {
73
- type: 'string'
74
- },
75
70
  snippet: {
76
71
  type: 'string'
77
72
  },
78
- to: {
79
- type: 'array',
80
- title: 'To',
81
- items: {
82
- type: 'string'
83
- }
84
- },
85
- cc: {
86
- type: 'array',
87
- title: 'Cc',
88
- items: {
89
- type: 'string'
90
- }
91
- },
92
73
  labels: {
93
74
  type: 'array',
94
75
  title: 'Labels',
95
76
  items: {
96
77
  type: 'string'
97
- }
98
- },
99
- sentAt: {
100
- $ref: '#/definitions/DateTime'
78
+ },
79
+ maxItems: SHORT_LIST_MAX_ITEMS
101
80
  },
102
81
  unread: {
103
82
  type: 'boolean'
@@ -107,14 +86,12 @@ export const googleMailMessageDefinition = {
107
86
  title: 'Attachments',
108
87
  items: {
109
88
  $ref: '#/definitions/Attachment'
110
- }
89
+ },
90
+ maxItems: SHORT_LIST_MAX_ITEMS
111
91
  },
112
92
  threadID: {
113
93
  type: 'string'
114
94
  },
115
- thread: {
116
- $ref: '#/definitions/ThreadDocID'
117
- },
118
95
  sourceService: {
119
96
  $ref: '#/definitions/SourceService'
120
97
  },
@@ -129,8 +106,6 @@ export const googleMailMessageDefinition = {
129
106
  }
130
107
  },
131
108
  required: [
132
- 'body',
133
- 'sender',
134
109
  'threadID',
135
110
  'thread',
136
111
  'sourceService',
@@ -140,7 +115,6 @@ export const googleMailMessageDefinition = {
140
115
  additionalProperties: false,
141
116
  definitions: {
142
117
  ...externalEntityDefinition.schema.definitions,
143
- ThreadDocID: docIDSchema,
144
118
  Attachment: {
145
119
  type: 'object',
146
120
  title: 'Attachment',
@@ -174,29 +148,22 @@ export const googleMailMessageDefinition = {
174
148
  }
175
149
  }
176
150
  },
177
- fieldsMeta: {
178
- thread: {
179
- type: 'document',
180
- model: '#0'
181
- }
182
- },
151
+ fieldsMeta: {},
183
152
  // lastSyncedAt is device-local sync bookkeeping; attachments carries the
184
153
  // locally-materialized `blob` annotation (absent until an explicit import
185
154
  // fills it), so the whole field stays outside the group rather than
186
155
  // splitting a single top-level field between provider and device content.
187
156
  providerMergePolicy: {
188
157
  group: [
189
- 'body',
190
- 'sender',
191
- 'subject',
158
+ ...messageKindProviderFields.slice(0, 2),
159
+ ...emailKindProviderFields.slice(0, 1),
192
160
  'snippet',
193
- 'to',
194
- 'cc',
161
+ ...emailKindProviderFields.slice(1),
195
162
  'labels',
196
- 'sentAt',
163
+ ...messageKindProviderFields.slice(2, 3),
197
164
  'unread',
198
165
  'threadID',
199
- 'thread',
166
+ ...messageKindProviderFields.slice(3),
200
167
  'sourceService',
201
168
  'sourceID',
202
169
  'sourceURL'
@@ -281,7 +248,7 @@ export const googleMailLabelDefinition = {
281
248
  const builder = new GraphBuilder({
282
249
  externalClusters: {
283
250
  ...connectorClusters,
284
- ...modelsClusters
251
+ ...messagingClusters
285
252
  }
286
253
  });
287
254
  builder.add(googleMailThreadDefinition); // #0
package/lib/provider.js CHANGED
@@ -1,6 +1,26 @@
1
1
  import { fetchOrAbsent, parseMaxAgeDays, resolveModelID } from '@kubun/connector';
2
+ import { mapConcurrent, retry } from '@sozai/async';
2
3
  import { buildHistoryListURL, buildLabelsListURL, buildMessagesListURL, buildProfileURL, getHeader, getLabel, getMessage, getThread, mailAPIClient, mapLabelToEntity, mapMessageToEntity, mapThreadToEntity } from './api.js';
3
4
  import { googleMailClusters } from './model.js';
5
+ // messages.get costs 5 quota units against Gmail's 250 units/s per-user limit.
6
+ const MESSAGE_FETCH_CONCURRENCY = 4;
7
+ // A burst can still trip Gmail's per-user rate limit; a limited message is
8
+ // retried on its own so the rest of the sync keeps going.
9
+ const MESSAGE_RATE_LIMIT_POLICY = {
10
+ maxAttempts: 6,
11
+ backoff: {
12
+ initialMs: 250,
13
+ multiplier: 2,
14
+ maxMs: 8000,
15
+ jitter: true
16
+ }
17
+ };
18
+ // 429, or Gmail's 403 rateLimitExceeded / userRateLimitExceeded.
19
+ function isRateLimited(error) {
20
+ const { httpStatus, message } = error ?? {};
21
+ if (httpStatus === 429) return true;
22
+ return httpStatus === 403 && typeof message === 'string' && /rate ?limit/i.test(message);
23
+ }
4
24
  export const DEFAULT_QUERY = 'in:inbox';
5
25
  const THREAD_MODEL_ID = resolveModelID(googleMailClusters, 'GoogleMailThread');
6
26
  const MESSAGE_MODEL_ID = resolveModelID(googleMailClusters, 'GoogleMailMessage');
@@ -51,10 +71,20 @@ export function createGoogleMailProvider(config) {
51
71
  // shared api.ts getMessage so both the bulk and targeted (fetchEntity)
52
72
  // paths share one 404-tagging choke point.
53
73
  async function getMessageResource(id, signal) {
54
- return getMessage({
55
- accessToken: config.credential.accessToken,
56
- messageID: id,
57
- fetch: fetchFn,
74
+ return retry(({ signal: attemptSignal })=>getMessage({
75
+ accessToken: config.credential.accessToken,
76
+ messageID: id,
77
+ fetch: fetchFn,
78
+ signal: attemptSignal
79
+ }), {
80
+ policy: MESSAGE_RATE_LIMIT_POLICY,
81
+ signal,
82
+ retryable: isRateLimited
83
+ });
84
+ }
85
+ function getMessageResources(ids, signal) {
86
+ return mapConcurrent(ids, (id, _index, callSignal)=>getMessageResource(id, callSignal), {
87
+ limit: MESSAGE_FETCH_CONCURRENCY,
58
88
  signal
59
89
  });
60
90
  }
@@ -90,10 +120,7 @@ export function createGoogleMailProvider(config) {
90
120
  const windowStart = windowStartSeconds(params.boundary, prior?.windowStart);
91
121
  const query = applyAfter(config.query ?? DEFAULT_QUERY, windowStart);
92
122
  const ids = await listMessageIDs(query, params.signal);
93
- const messages = [];
94
- for (const { id } of ids){
95
- messages.push(await getMessageResource(id, params.signal));
96
- }
123
+ const messages = await getMessageResources(ids.map(({ id })=>id), params.signal);
97
124
  // Thread batch: one thread per distinct threadId, titled by its first message's subject.
98
125
  const threadSubjects = new Map();
99
126
  for (const m of messages){
@@ -176,10 +203,7 @@ export function createGoogleMailProvider(config) {
176
203
  const survivingIDs = [
177
204
  ...addedIDs.keys()
178
205
  ].filter((id)=>!deletedIDs.has(id));
179
- const messages = [];
180
- for (const id of survivingIDs){
181
- messages.push(await getMessageResource(id, params.signal));
182
- }
206
+ const messages = await getMessageResources(survivingIDs, params.signal);
183
207
  const threadSubjects = new Map();
184
208
  for (const m of messages){
185
209
  if (!threadSubjects.has(m.threadId)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/connector-google-mail",
3
- "version": "0.17.3",
3
+ "version": "0.18.0",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,14 +15,16 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@kubun/connector": "^0.17.3",
19
- "@kubun/credential-types": "^0.17.2",
20
- "@kubun/hlc": "^0.17.0",
21
- "@kubun/models": "^0.17.0",
22
- "@kubun/protocol": "^0.17.2",
23
- "@kubun/provider-google": "^0.17.1",
24
- "@kubun/scalars": "^0.17.0",
25
- "@sozai/codec": "^0.4.0"
18
+ "@kubun/connector": "^0.18.0",
19
+ "@kubun/credential-types": "^0.18.0",
20
+ "@kubun/hlc": "^0.18.0",
21
+ "@kubun/models-core": "^0.18.0",
22
+ "@kubun/models-messaging": "^0.18.0",
23
+ "@kubun/protocol": "^0.18.0",
24
+ "@kubun/provider-google": "^0.18.0",
25
+ "@kubun/scalars": "^0.18.0",
26
+ "@sozai/async": "^0.4.1",
27
+ "@sozai/codec": "^0.4.3"
26
28
  },
27
29
  "publishConfig": {
28
30
  "access": "public"