@kubun/connector-google-mail 0.17.0 → 0.17.1

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/provider.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { Credential } from '@kubun/credential-types';
3
3
  export declare const DEFAULT_QUERY = "in:inbox";
4
4
  export type MailCheckpoint = {
5
5
  historyId: string;
6
+ windowStart?: number;
6
7
  };
7
8
  export type GoogleMailProviderConfig = {
8
9
  credential: Credential;
package/lib/provider.js CHANGED
@@ -17,12 +17,22 @@ async function fetchJSON(url, credential, fetchFn, signal) {
17
17
  }
18
18
  return await response.json();
19
19
  }
20
- function applyMaxAge(query, boundary) {
21
- if (boundary?.maxAge == null) return query;
20
+ /**
21
+ * The absolute lower bound (epoch seconds) for a full message listing: a window
22
+ * already frozen in the checkpoint, else `now − maxAge` computed once. Null when
23
+ * the boundary carries no `maxAge` (no floor).
24
+ */ function windowStartSeconds(boundary, frozen) {
25
+ if (frozen != null) return frozen;
26
+ if (boundary?.maxAge == null) return null;
22
27
  const match = /^P(\d+)D$/i.exec(boundary.maxAge);
23
- if (match == null) return query;
28
+ if (match?.[1] == null) return null;
29
+ const days = Number.parseInt(match[1], 10);
30
+ return Math.floor((Date.now() - days * 86_400_000) / 1000);
31
+ }
32
+ /** Appends an absolute `after:<epoch seconds>` floor to a Gmail search query. */ function applyAfter(query, windowStart) {
33
+ if (windowStart == null) return query;
24
34
  if (/\b(newer_than:|after:)/i.test(query)) return query;
25
- return `${query} newer_than:${match[1]}d`;
35
+ return `${query} after:${windowStart}`;
26
36
  }
27
37
  function maxHistoryId(messages) {
28
38
  let max = -1;
@@ -69,7 +79,11 @@ export function createGoogleMailProvider(config) {
69
79
  }
70
80
  async function* fetchAllEntities(params) {
71
81
  yield await labelBatch(params.signal);
72
- const query = applyMaxAge(config.query ?? DEFAULT_QUERY, params.boundary);
82
+ // Reuse a window frozen by a prior sync (carried in a checkpoint on the 404
83
+ // resync path, or a re-driven initial); only a genuine first sync computes it.
84
+ const prior = params.checkpoint;
85
+ const windowStart = windowStartSeconds(params.boundary, prior?.windowStart);
86
+ const query = applyAfter(config.query ?? DEFAULT_QUERY, windowStart);
73
87
  const ids = await listMessageIDs(query, params.signal);
74
88
  const messages = [];
75
89
  for (const { id } of ids){
@@ -90,7 +104,10 @@ export function createGoogleMailProvider(config) {
90
104
  hasMore: true
91
105
  };
92
106
  const checkpoint = {
93
- historyId: maxHistoryId(messages) ?? await profileHistoryId(params.signal)
107
+ historyId: maxHistoryId(messages) ?? await profileHistoryId(params.signal),
108
+ ...windowStart != null ? {
109
+ windowStart
110
+ } : {}
94
111
  };
95
112
  yield {
96
113
  modelID: MESSAGE_MODEL_ID,
@@ -176,7 +193,10 @@ export function createGoogleMailProvider(config) {
176
193
  })),
177
194
  hasMore: false,
178
195
  checkpoint: {
179
- historyId: latestHistoryId
196
+ historyId: latestHistoryId,
197
+ ...checkpoint.windowStart != null ? {
198
+ windowStart: checkpoint.windowStart
199
+ } : {}
180
200
  }
181
201
  };
182
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/connector-google-mail",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,7 +15,7 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@kubun/connector": "^0.17.0",
18
+ "@kubun/connector": "^0.17.1",
19
19
  "@kubun/credential-types": "^0.17.0",
20
20
  "@kubun/models": "^0.17.0",
21
21
  "@kubun/protocol": "^0.17.0",