@framers/agentos-ext-gmail-source 0.3.0 → 0.4.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/README.md +39 -1
- package/dist/gmail.d.ts +182 -0
- package/dist/gmail.d.ts.map +1 -0
- package/dist/gmail.js +568 -0
- package/dist/gmail.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/retry.d.ts +44 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +102 -0
- package/dist/retry.js.map +1 -0
- package/manifest.json +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Gmail source
|
|
2
2
|
|
|
3
|
-
This library provides read-only Gmail authorization on the AgentOS redirect grant and sealed-token contracts, text helpers that sanitize and trim mail before it is stored (see Text ingestion below),
|
|
3
|
+
This library provides read-only Gmail authorization on the AgentOS redirect grant and sealed-token contracts, text helpers that sanitize and trim mail before it is stored (see Text ingestion below), a bounded mapper that turns one thread response into a sanitized source document (see Thread documents below), and a capped read-only API client that sends every call through the host's quota and grant services (see Read-only API below). It requests the sole Gmail read-only scope, offline access and incremental consent. It exports no send, reply, label-modification or attachment-download tool.
|
|
4
4
|
|
|
5
5
|
Install it next to `@framers/agentos` 0.13.134 or later and `@framers/agentos-ext-account-sources` 0.7.0 or later. Both are peer dependencies with a floor and no upper bound. Node.js 20.19 or later is required.
|
|
6
6
|
|
|
@@ -94,3 +94,41 @@ Attachments are listed; their contents are not read. An external body or attachm
|
|
|
94
94
|
A returned document has matching item/ref versions, a normalized-content hash, message ids and body spans, earliest allowed expiry and explicit omission/truncation metadata. Spans count UTF-16 code units in the returned text and exclude the header prefix. Quote trimming preserves normalized source text; no generated summary or ellipsis is inserted into a quote. Header-only messages can have an empty body span. Render structured metadata as escaped text and treat all mail as untrusted data.
|
|
95
95
|
|
|
96
96
|
A history-only change does not extend cache life or change the normalized hash. Storage still requires the account-source transactional fence, fresh expiry checks and host allowances. Derived results retain the whole source's personal-use, expiry and deletion restrictions. Never persist the transient MIME types or bypass the account-source store with a mapper result.
|
|
97
|
+
|
|
98
|
+
## Read-only API
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { GmailApi } from '@framers/agentos-ext-gmail-source';
|
|
102
|
+
|
|
103
|
+
// `revocations` is the host's SourceRevocations and `quota` its SourceQuota, on the tables its coordinator uses.
|
|
104
|
+
const api = new GmailApi({
|
|
105
|
+
client, revocations, quota,
|
|
106
|
+
currentHandle: () => handle, // the sync handle that holds this connection's lease now
|
|
107
|
+
projectKey: 'project-1', mailboxKey, // a stable keyed hash for the mailbox, never its address
|
|
108
|
+
bounds, // the thread mapper's bounds (see Thread documents)
|
|
109
|
+
limits: { timeoutMs: 15_000, maxLabels: 1_000, maxHistoryReferences: 10_000, maxPageTokenCharacters: 4_096 },
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const profile = await api.profile(signal); // compare profile.emailAddress privately; keep profile.historyId
|
|
113
|
+
const labels = await api.labels(permission, signal);
|
|
114
|
+
const page = await api.list(cutoff, 30 * 86_400_000, 100, null, signal); // { kind: 'scan', ids, nextPageToken }
|
|
115
|
+
for (const id of page.ids) {
|
|
116
|
+
const result = await api.readThread(id, {
|
|
117
|
+
cutoff, windowMs: 30 * 86_400_000, sourceDetailUrl: detailPath(id), permission, labels, signal,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const changes = await api.history(profile.historyId, 100, null, signal); // { kind: 'history', ids, nextPageToken, cursor }
|
|
121
|
+
// A failure is a SourceSyncFault with a fixed code and, for a wait, the time to try again in retryAt.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`GmailApi(options)` uses the account-source quota and grant services supplied by the host. Pass a live `currentHandle` getter, stable opaque project/mailbox quota keys, the dedicated OAuth client, mapper bounds and explicit transport limits. The client constructs each OAuth flow inside the grant service's callback. It retains no flow or sealed-store callback between operations. Production uses fixed Google origins and only profile, labels, threads list/get and history list. CI may configure loopback or reserved fixture origins; HTTP redirects and arbitrary paths are refused.
|
|
125
|
+
|
|
126
|
+
`profile(signal?)` returns a server-memory address and history id for private account binding. Never store its address in a source or log it. `list(cutoff, windowMs, pageSize, pageToken, signal?)` returns a bounded scan page. The query uses epoch seconds and excludes spam, trash and drafts; exact message eligibility remains the mapper's responsibility. `history(fromCursor, pageSize, pageToken, signal?)` returns deduplicated changed thread ids and the string response cursor. The host must persist pending work and commit a terminal cursor only after all work has completed. Neither method claims mailbox-wide completeness.
|
|
127
|
+
|
|
128
|
+
`labels(permission, signal?)` returns sanitized names and the cache decision from that labels response. A refused decision contains no names. `readThread(sourceId, options)` accepts cutoff/window, a trusted private detail path, cache permission, the independent label snapshot and optional signal. It passes the actual thread response headers and times to the mapper and returns only the account-source read-result union. It never returns raw MIME. A labels response cannot borrow a thread response's longer cache lifetime.
|
|
129
|
+
|
|
130
|
+
One provider operation makes one attempt, with one fresh-token retry permitted after 401. Every HTTP dispatch is separately metered. Token refreshes also use the one-unit profile reservation as conservative local accounting; this does not assert a Gmail quota cost for the OAuth endpoint. The durable account-source coordinator owns retry episodes, exponential backoff and exhaustion. The client persists provider waits through the quota service and never sleeps or buys capacity. A quota deferral, or a start that missed the quota's send window, sends nothing and throws `rate_limited` with the time to try again; only a reused attempt id is `invalid_response`. A retry time the client hands back is always after its own clock, even when the quota's database clock lags it; give the client the clock the host's sync store reads (`Date.now` for both unless given). Unknown errors become fixed failures without retaining their messages or causes.
|
|
131
|
+
|
|
132
|
+
A canonical thread 404 means gone. History 404 means the history anchor expired and bounded recovery is required. Domain policy is a fixed failure. Missing or explicitly invalid grants and insufficient scope are passed to the coordinator's fenced grant-loss lifecycle. Client configuration errors and transient network/provider failures are not reported as revoked mailboxes. Oversized successful thread responses are counted skips; oversized control pages fail without a cursor advance. Response streams are capped before decoding and are cancelled on size, deadline or caller cancellation.
|
|
133
|
+
|
|
134
|
+
The client performs no storage write, purge, logging, model request or attachment fetch. Its successful result is still untrusted source material and needs the host's fresh owner/generation/expiry checks before storage, preparation or delivery. Limits are defensive library policy, not provider maxima; hosts must record any live compatibility failure before changing them.
|
package/dist/gmail.d.ts
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { type SourceQuota, type SourceRevocations, type SourceSyncHandle, type SourceSyncScanPage, type SourceSyncHistoryPage, type SourceSyncReadResult, type SourceCachePermission, type SourceCacheDecision } from '@framers/agentos-ext-account-sources';
|
|
2
|
+
import type { GmailOAuthClient, GmailOAuthEndpoints } from './types.js';
|
|
3
|
+
import { type GmailMappingBounds } from './mime.js';
|
|
4
|
+
/**
|
|
5
|
+
* The client's defensive transport limits, each a positive safe integer no greater than the value in brackets. They
|
|
6
|
+
* are library choices, not Google maxima: a control page past one fails without a partial page.
|
|
7
|
+
*/
|
|
8
|
+
export interface GmailApiLimits {
|
|
9
|
+
/** Milliseconds one call and the reading of its body may take (15,000). */
|
|
10
|
+
readonly timeoutMs: number;
|
|
11
|
+
/** Labels one labels response may hold (1,000). */
|
|
12
|
+
readonly maxLabels: number;
|
|
13
|
+
/** Message references one history page may hold across its five event fields (10,000). */
|
|
14
|
+
readonly maxHistoryReferences: number;
|
|
15
|
+
/** Characters of one page token, given or returned (4,096). */
|
|
16
|
+
readonly maxPageTokenCharacters: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* What a `GmailApi` is built from. Every value comes from trusted server configuration and the host's released
|
|
20
|
+
* account-source services; none comes from a browser request.
|
|
21
|
+
*
|
|
22
|
+
* @typeParam Prepared - The host's prepared-document type of its `SourceRevocations`.
|
|
23
|
+
*/
|
|
24
|
+
export interface GmailApiOptions<Prepared = unknown> {
|
|
25
|
+
/** The dedicated OAuth client the grant was issued to. */
|
|
26
|
+
readonly client: GmailOAuthClient;
|
|
27
|
+
/** The host's revocation service; its `withGrant` serializes every refresh under the durable grant lease. */
|
|
28
|
+
readonly revocations: Pick<SourceRevocations<Prepared>, 'withGrant'>;
|
|
29
|
+
/** Answers the sync handle current for this connection at each call, so a superseded handle is refused. */
|
|
30
|
+
readonly currentHandle: () => SourceSyncHandle;
|
|
31
|
+
/** The shared quota: each token refresh and each HTTP dispatch is one `run`, and provider waits go to `defer`. */
|
|
32
|
+
readonly quota: Pick<SourceQuota, 'run' | 'defer'>;
|
|
33
|
+
/** The opaque quota key of the project, 1 to 512 letters, digits, `:`, `_` or `-`. */
|
|
34
|
+
readonly projectKey: string;
|
|
35
|
+
/** Stable host HMAC key, never an address or a rotating connection id. */
|
|
36
|
+
readonly mailboxKey: string;
|
|
37
|
+
/** The thread mapper's bounds; `maxResponseBytes` also caps every API response before decoding. */
|
|
38
|
+
readonly bounds: GmailMappingBounds;
|
|
39
|
+
/** The transport and control-page limits. */
|
|
40
|
+
readonly limits: GmailApiLimits;
|
|
41
|
+
/** The fetch every token and API call goes through; the global fetch by default. */
|
|
42
|
+
readonly fetchImpl?: typeof fetch;
|
|
43
|
+
/**
|
|
44
|
+
* Epoch milliseconds; `Date.now` unless given. Give it the clock the host's sync store reads: a retry time this
|
|
45
|
+
* client hands back is always at least one millisecond after it, whichever clock produced the time.
|
|
46
|
+
*/
|
|
47
|
+
readonly clock?: () => number;
|
|
48
|
+
/** CI-only transport endpoints. No request-supplied origin or client value. */
|
|
49
|
+
readonly apiOrigin?: string;
|
|
50
|
+
/** CI-only OAuth stand-in endpoints, checked as `GmailOAuthFlow` checks them. */
|
|
51
|
+
readonly oauthEndpoints?: GmailOAuthEndpoints;
|
|
52
|
+
}
|
|
53
|
+
/** Server-memory identity result. Never serialize it into a source or telemetry. */
|
|
54
|
+
export interface GmailProfile {
|
|
55
|
+
/** The mailbox address, for the host's private comparison only. */
|
|
56
|
+
readonly emailAddress: string;
|
|
57
|
+
/** The mailbox's current history id as a decimal string. */
|
|
58
|
+
readonly historyId: string;
|
|
59
|
+
}
|
|
60
|
+
/** Sanitized label names and the labels response's own cache decision; a refused decision holds no names. */
|
|
61
|
+
export interface GmailLabels {
|
|
62
|
+
/** Sanitized names by label id. */
|
|
63
|
+
readonly names: Readonly<Record<string, string>>;
|
|
64
|
+
/** The decision for the labels response, independent of any thread response. */
|
|
65
|
+
readonly cache: SourceCacheDecision;
|
|
66
|
+
}
|
|
67
|
+
/** The per-read values of one `readThread` call, passed to the thread mapper with the response's times and headers. */
|
|
68
|
+
export interface GmailReadOptions {
|
|
69
|
+
/** The selection cutoff in epoch milliseconds. */
|
|
70
|
+
readonly cutoff: number;
|
|
71
|
+
/** The message window in milliseconds. */
|
|
72
|
+
readonly windowMs: number;
|
|
73
|
+
/** The host's authenticated relative source-detail path; never a Gmail link. */
|
|
74
|
+
readonly sourceDetailUrl: string;
|
|
75
|
+
/** The host's evidenced cache permission; null refuses durable content. */
|
|
76
|
+
readonly permission: SourceCachePermission | null;
|
|
77
|
+
/** The cycle's label snapshot from `labels`, or null to keep label ids without names. */
|
|
78
|
+
readonly labels: GmailLabels | null;
|
|
79
|
+
/** Cancels the read. */
|
|
80
|
+
readonly signal?: AbortSignal;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A client's fixed refusal; its message is `gmail_api_<reason>`. `configuration` is thrown by the constructor;
|
|
84
|
+
* `response_too_large` stays inside the client, which answers it as a skipped thread or an invalid control page.
|
|
85
|
+
*/
|
|
86
|
+
export declare class GmailApiFault extends Error {
|
|
87
|
+
readonly reason: 'configuration' | 'response_too_large';
|
|
88
|
+
/** @param reason Why the client refused. */
|
|
89
|
+
constructor(reason: 'configuration' | 'response_too_large');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The capped, read-only Gmail API client. Each provider operation makes one attempt: one OAuth refresh inside the
|
|
93
|
+
* host's grant operation, then one HTTP dispatch, each admitted by the shared quota with a new attempt id; a 401 earns
|
|
94
|
+
* one fresh token and one repeat of the same call. Provider waits are recorded through the quota's `defer` and thrown
|
|
95
|
+
* as `rate_limited` or `provider_unavailable` with their time; the durable coordinator owns retry episodes, backoff
|
|
96
|
+
* and exhaustion. Every failure is a fixed `SourceSyncFault`.
|
|
97
|
+
*
|
|
98
|
+
* @typeParam Prepared - The host's prepared-document type of its `SourceRevocations`.
|
|
99
|
+
*/
|
|
100
|
+
export declare class GmailApi<Prepared = unknown> {
|
|
101
|
+
private readonly options;
|
|
102
|
+
private readonly origin;
|
|
103
|
+
private readonly send;
|
|
104
|
+
private readonly bounds;
|
|
105
|
+
private readonly limits;
|
|
106
|
+
private readonly mapper;
|
|
107
|
+
/**
|
|
108
|
+
* @param options The client, the host's grant and quota services, the quota keys, the bounds and limits, and the
|
|
109
|
+
* optional fetch, clock and CI-only endpoints.
|
|
110
|
+
* @throws {GmailApiFault} `configuration` for an origin override outside CI or not a loopback or `.invalid` origin,
|
|
111
|
+
* a limit outside its ceiling, or a malformed quota key.
|
|
112
|
+
* @throws {GmailMappingFault} `configuration` for invalid bounds.
|
|
113
|
+
*/
|
|
114
|
+
constructor(options: GmailApiOptions<Prepared>);
|
|
115
|
+
private now;
|
|
116
|
+
private later;
|
|
117
|
+
private pageToken;
|
|
118
|
+
private pageSize;
|
|
119
|
+
private quotaValue;
|
|
120
|
+
private reserve;
|
|
121
|
+
private defer;
|
|
122
|
+
private accessToken;
|
|
123
|
+
/** Consume/cancel the whole body within the quota callback, before releasing its slot. */
|
|
124
|
+
private wire;
|
|
125
|
+
private request;
|
|
126
|
+
private control;
|
|
127
|
+
/**
|
|
128
|
+
* The mailbox address and current history id, for the host's private account binding and its history anchor. The
|
|
129
|
+
* answer's other fields are dropped.
|
|
130
|
+
*
|
|
131
|
+
* @param signal Cancels the call.
|
|
132
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for a malformed answer.
|
|
133
|
+
*/
|
|
134
|
+
profile(signal?: AbortSignal): Promise<GmailProfile>;
|
|
135
|
+
/**
|
|
136
|
+
* The mailbox's labels: sanitized names by id under the labels response's own cache decision. A refused decision
|
|
137
|
+
* keeps no names.
|
|
138
|
+
*
|
|
139
|
+
* @param permission The host's evidenced cache permission; null refuses the names.
|
|
140
|
+
* @param signal Cancels the call.
|
|
141
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for a malformed or oversized list.
|
|
142
|
+
*/
|
|
143
|
+
labels(permission: SourceCachePermission | null, signal?: AbortSignal): Promise<GmailLabels>;
|
|
144
|
+
/**
|
|
145
|
+
* One page of candidate thread ids between `cutoff - windowMs` and `cutoff`, outside spam, trash and drafts. The
|
|
146
|
+
* query's whole-second bounds are widened by a second each way; the mapper enforces the exact window. Snippets and
|
|
147
|
+
* estimates are dropped, and no mailbox-wide completeness is claimed.
|
|
148
|
+
*
|
|
149
|
+
* @param cutoff The selection cutoff in epoch milliseconds.
|
|
150
|
+
* @param windowMs The window, from 1 millisecond to 90 days.
|
|
151
|
+
* @param pageSize Candidates per page, from 1 to 100.
|
|
152
|
+
* @param pageToken The resume token from the previous page, or null for the first.
|
|
153
|
+
* @param signal Cancels the call.
|
|
154
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for bad arguments, a malformed page or a repeated
|
|
155
|
+
* page token.
|
|
156
|
+
*/
|
|
157
|
+
list(cutoff: number, windowMs: number, pageSize: number, pageToken: string | null, signal?: AbortSignal): Promise<SourceSyncScanPage>;
|
|
158
|
+
/**
|
|
159
|
+
* One page of history after `fromCursor`: the thread ids of every message addition, deletion and label change, and
|
|
160
|
+
* of generic message entries, deduplicated and sorted, with the page's decimal-string cursor. The host persists the
|
|
161
|
+
* pending work and commits a terminal cursor only once every item is terminal.
|
|
162
|
+
*
|
|
163
|
+
* @param fromCursor The committed history id, a decimal string.
|
|
164
|
+
* @param pageSize Records per page, from 1 to 100.
|
|
165
|
+
* @param pageToken The resume token from the previous page, or null for the first.
|
|
166
|
+
* @param signal Cancels the call.
|
|
167
|
+
* @throws {SourceSyncFault} `history_expired` when Google no longer holds the anchor; `invalid_response` for bad
|
|
168
|
+
* arguments, a malformed or out-of-order page, too many references or a repeated page token; another fixed failure.
|
|
169
|
+
*/
|
|
170
|
+
history(fromCursor: string, pageSize: number, pageToken: string | null, signal?: AbortSignal): Promise<SourceSyncHistoryPage>;
|
|
171
|
+
/**
|
|
172
|
+
* Reads one thread in the `full` format and maps it with its own read times and cache headers and the separate
|
|
173
|
+
* label snapshot. A 404 is `gone`; an oversized successful answer is skipped as `response_too_large`; any other
|
|
174
|
+
* result is the mapper's. The raw response never leaves the client.
|
|
175
|
+
*
|
|
176
|
+
* @param sourceId The thread id, 1 to 64 hexadecimal characters.
|
|
177
|
+
* @param options The cutoff, window, detail path, cache permission, label snapshot and signal.
|
|
178
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for a malformed id.
|
|
179
|
+
*/
|
|
180
|
+
readThread(sourceId: string, options: GmailReadOptions): Promise<SourceSyncReadResult>;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=gmail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmail.d.ts","sourceRoot":"","sources":["../src/gmail.ts"],"names":[],"mappings":"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"}
|
package/dist/gmail.js
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The capped, read-only Gmail API client: profile, labels, a bounded thread list, history pages and one
|
|
3
|
+
* thread read at fixed `users/me` paths. Every token refresh and every HTTP dispatch is admitted through the host's
|
|
4
|
+
* shared account-source quota with a new attempt id, and every refresh runs inside the host's grant operation on a
|
|
5
|
+
* flow built for it alone. Responses are read through the released byte cap before decoding, with prompt cancellation,
|
|
6
|
+
* and only fixed failures leave the client. It never sleeps, retries a transient failure inline, stores, purges, logs,
|
|
7
|
+
* or fetches an attachment.
|
|
8
|
+
* @module agentos-ext-gmail-source/gmail
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { readCapped, ReadCapExceeded, decideSourceCache, sourceRetryAt, SourceSyncFault, SOURCE_GRANT_KEY, } from '@framers/agentos-ext-account-sources';
|
|
12
|
+
import { GmailOAuthFlow } from './GmailOAuthFlow.js';
|
|
13
|
+
import { GmailThreadMapper } from './mapThread.js';
|
|
14
|
+
import { mappingBounds, gmailId } from './mime.js';
|
|
15
|
+
import { sanitizeMailMetadata } from './mailSanitize.js';
|
|
16
|
+
import { classifyGmailHttp, gmailOAuthFault } from './retry.js';
|
|
17
|
+
/**
|
|
18
|
+
* A client's fixed refusal; its message is `gmail_api_<reason>`. `configuration` is thrown by the constructor;
|
|
19
|
+
* `response_too_large` stays inside the client, which answers it as a skipped thread or an invalid control page.
|
|
20
|
+
*/
|
|
21
|
+
export class GmailApiFault extends Error {
|
|
22
|
+
reason;
|
|
23
|
+
/** @param reason Why the client refused. */
|
|
24
|
+
constructor(reason) {
|
|
25
|
+
super(`gmail_api_${reason}`);
|
|
26
|
+
this.reason = reason;
|
|
27
|
+
this.name = 'GmailApiFault';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const productionOrigin = 'https://gmail.googleapis.com';
|
|
31
|
+
// The latest epoch millisecond the account-source services accept.
|
|
32
|
+
const maxTime = 8_639_999_913_600_000;
|
|
33
|
+
const integer = (n) => Number.isSafeInteger(n) && n >= 0 && n <= maxTime;
|
|
34
|
+
// An invalid provider shape or argument is a fixed failure, never an empty success.
|
|
35
|
+
const invalid = () => { throw new SourceSyncFault('invalid_response'); };
|
|
36
|
+
const historyId = (value) => typeof value === 'string' && /^[0-9]{1,128}$/.test(value);
|
|
37
|
+
const labelId = (value) => typeof value === 'string'
|
|
38
|
+
&& /^(?:[A-Z][A-Z0-9_]{0,63}|Label_[0-9]{1,64})$/.test(value);
|
|
39
|
+
// Orders decimal history ids as strings, by length and then lexically, so no id passes through a float.
|
|
40
|
+
function compareHistory(a, b) {
|
|
41
|
+
const left = a.replace(/^0+(?=.)/, '');
|
|
42
|
+
const right = b.replace(/^0+(?=.)/, '');
|
|
43
|
+
return left.length - right.length || (left < right ? -1 : left > right ? 1 : 0);
|
|
44
|
+
}
|
|
45
|
+
function object(value) {
|
|
46
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
47
|
+
return invalid();
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
function array(value, maximum) {
|
|
51
|
+
if (!Array.isArray(value) || value.length > maximum)
|
|
52
|
+
return invalid();
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
// Google's origin, or on CI only a loopback or `.invalid` origin with no path, query, fragment or userinfo.
|
|
56
|
+
function selectedOrigin(value) {
|
|
57
|
+
if (value === undefined)
|
|
58
|
+
return productionOrigin;
|
|
59
|
+
let url;
|
|
60
|
+
try {
|
|
61
|
+
url = new URL(value);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new GmailApiFault('configuration');
|
|
65
|
+
}
|
|
66
|
+
const local = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
67
|
+
if (process.env.CI !== 'true' || (!local && !url.hostname.endsWith('.invalid'))
|
|
68
|
+
|| (url.protocol !== 'https:' && !(local && url.protocol === 'http:'))
|
|
69
|
+
|| url.username || url.password || url.pathname !== '/' || url.search || url.hash) {
|
|
70
|
+
throw new GmailApiFault('configuration');
|
|
71
|
+
}
|
|
72
|
+
return url.origin;
|
|
73
|
+
}
|
|
74
|
+
function cancel(response) { void response.body?.cancel().catch(() => { }); }
|
|
75
|
+
function active(signal) { if (signal?.aborted)
|
|
76
|
+
throw new SourceSyncFault('cancelled'); }
|
|
77
|
+
// Rejects promptly on abort; a response that arrives afterwards is handed to `discard` instead of the caller.
|
|
78
|
+
function abortable(work, signal, stopped, discard) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
let done = false;
|
|
81
|
+
const abort = () => { if (!done) {
|
|
82
|
+
done = true;
|
|
83
|
+
reject(stopped());
|
|
84
|
+
} };
|
|
85
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
86
|
+
if (signal.aborted)
|
|
87
|
+
abort();
|
|
88
|
+
work.then((value) => {
|
|
89
|
+
signal.removeEventListener('abort', abort);
|
|
90
|
+
if (done) {
|
|
91
|
+
discard?.(value);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
done = true;
|
|
95
|
+
resolve(value);
|
|
96
|
+
}, (error) => {
|
|
97
|
+
signal.removeEventListener('abort', abort);
|
|
98
|
+
if (!done) {
|
|
99
|
+
done = true;
|
|
100
|
+
reject(error);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// Reads a body through the released byte cap. The bridge turns an abort into a fixed fault even while no bytes
|
|
106
|
+
// arrive, and passes cancellation through to the provider's stream.
|
|
107
|
+
async function bodyBytes(response, maximum, signal, stopped) {
|
|
108
|
+
if (signal.aborted) {
|
|
109
|
+
cancel(response);
|
|
110
|
+
throw stopped();
|
|
111
|
+
}
|
|
112
|
+
if (response.body === null)
|
|
113
|
+
return new Uint8Array(0);
|
|
114
|
+
const reader = response.body.getReader();
|
|
115
|
+
let closed = false;
|
|
116
|
+
let stop;
|
|
117
|
+
const bridge = new ReadableStream({
|
|
118
|
+
start(controller) {
|
|
119
|
+
stop = () => {
|
|
120
|
+
if (closed)
|
|
121
|
+
return;
|
|
122
|
+
closed = true;
|
|
123
|
+
controller.error(stopped());
|
|
124
|
+
void reader.cancel().catch(() => { });
|
|
125
|
+
};
|
|
126
|
+
signal.addEventListener('abort', stop, { once: true });
|
|
127
|
+
if (signal.aborted)
|
|
128
|
+
stop();
|
|
129
|
+
},
|
|
130
|
+
async pull(controller) {
|
|
131
|
+
try {
|
|
132
|
+
const next = await reader.read();
|
|
133
|
+
if (closed)
|
|
134
|
+
return;
|
|
135
|
+
if (next.done) {
|
|
136
|
+
closed = true;
|
|
137
|
+
controller.close();
|
|
138
|
+
}
|
|
139
|
+
else
|
|
140
|
+
controller.enqueue(next.value);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
if (!closed) {
|
|
144
|
+
closed = true;
|
|
145
|
+
controller.error(signal.aborted ? stopped() : new SourceSyncFault('network'));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
async cancel() { closed = true; try {
|
|
150
|
+
await reader.cancel();
|
|
151
|
+
}
|
|
152
|
+
catch { /* Fixed fault only. */ } },
|
|
153
|
+
});
|
|
154
|
+
try {
|
|
155
|
+
return await readCapped(new Response(bridge, { headers: response.headers }), maximum);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
signal.removeEventListener('abort', stop);
|
|
159
|
+
try {
|
|
160
|
+
reader.releaseLock();
|
|
161
|
+
}
|
|
162
|
+
catch { /* Cancellation can still be settling. */ }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The capped, read-only Gmail API client. Each provider operation makes one attempt: one OAuth refresh inside the
|
|
167
|
+
* host's grant operation, then one HTTP dispatch, each admitted by the shared quota with a new attempt id; a 401 earns
|
|
168
|
+
* one fresh token and one repeat of the same call. Provider waits are recorded through the quota's `defer` and thrown
|
|
169
|
+
* as `rate_limited` or `provider_unavailable` with their time; the durable coordinator owns retry episodes, backoff
|
|
170
|
+
* and exhaustion. Every failure is a fixed `SourceSyncFault`.
|
|
171
|
+
*
|
|
172
|
+
* @typeParam Prepared - The host's prepared-document type of its `SourceRevocations`.
|
|
173
|
+
*/
|
|
174
|
+
export class GmailApi {
|
|
175
|
+
options;
|
|
176
|
+
origin;
|
|
177
|
+
send;
|
|
178
|
+
bounds;
|
|
179
|
+
limits;
|
|
180
|
+
mapper;
|
|
181
|
+
/**
|
|
182
|
+
* @param options The client, the host's grant and quota services, the quota keys, the bounds and limits, and the
|
|
183
|
+
* optional fetch, clock and CI-only endpoints.
|
|
184
|
+
* @throws {GmailApiFault} `configuration` for an origin override outside CI or not a loopback or `.invalid` origin,
|
|
185
|
+
* a limit outside its ceiling, or a malformed quota key.
|
|
186
|
+
* @throws {GmailMappingFault} `configuration` for invalid bounds.
|
|
187
|
+
*/
|
|
188
|
+
constructor(options) {
|
|
189
|
+
this.options = options;
|
|
190
|
+
this.origin = selectedOrigin(options.apiOrigin);
|
|
191
|
+
this.send = options.fetchImpl ?? fetch;
|
|
192
|
+
this.bounds = mappingBounds(options.bounds);
|
|
193
|
+
this.mapper = new GmailThreadMapper(this.bounds);
|
|
194
|
+
const ceilings = { timeoutMs: 15_000, maxLabels: 1000, maxHistoryReferences: 10_000, maxPageTokenCharacters: 4096 };
|
|
195
|
+
for (const key of Object.keys(ceilings)) {
|
|
196
|
+
if (!integer(options.limits[key]) || options.limits[key] < 1 || options.limits[key] > ceilings[key]) {
|
|
197
|
+
throw new GmailApiFault('configuration');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this.limits = Object.freeze({ ...options.limits });
|
|
201
|
+
for (const key of [options.projectKey, options.mailboxKey]) {
|
|
202
|
+
if (typeof key !== 'string' || !/^[A-Za-z0-9:_-]{1,512}$/.test(key))
|
|
203
|
+
throw new GmailApiFault('configuration');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
now() {
|
|
207
|
+
const value = this.options.clock?.() ?? Date.now();
|
|
208
|
+
if (!integer(value))
|
|
209
|
+
return invalid();
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
212
|
+
// A retry time handed back: never at or before this client's clock. The quota's times come from the database's
|
|
213
|
+
// clock, and the sync store refuses a retry time that is not after its own.
|
|
214
|
+
later(retryAt) { return Math.max(retryAt, this.now() + 1); }
|
|
215
|
+
pageToken(value) {
|
|
216
|
+
if (value === undefined || value === null)
|
|
217
|
+
return null;
|
|
218
|
+
if (typeof value !== 'string' || value.length < 1 || value.length > this.limits.maxPageTokenCharacters
|
|
219
|
+
|| /[\x00-\x20\x7f]/.test(value))
|
|
220
|
+
return invalid();
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
pageSize(value) { if (!Number.isSafeInteger(value) || value < 1 || value > 100)
|
|
224
|
+
invalid(); }
|
|
225
|
+
// Nothing was sent for a deferral or a late start: both are retryable from their time, with a new attempt id. Only
|
|
226
|
+
// a reused attempt id is a fault.
|
|
227
|
+
quotaValue(result) {
|
|
228
|
+
if (result.kind === 'deferred' || result.kind === 'late')
|
|
229
|
+
throw new SourceSyncFault('rate_limited', this.later(result.retryAt));
|
|
230
|
+
if (result.kind === 'duplicate')
|
|
231
|
+
return invalid();
|
|
232
|
+
return result.value;
|
|
233
|
+
}
|
|
234
|
+
async reserve(method, work, parent) {
|
|
235
|
+
active(parent);
|
|
236
|
+
const result = await this.options.quota.run({ projectKey: this.options.projectKey,
|
|
237
|
+
mailboxKey: this.options.mailboxKey, method, attemptId: randomUUID() }, work, parent);
|
|
238
|
+
active(parent);
|
|
239
|
+
return this.quotaValue(result);
|
|
240
|
+
}
|
|
241
|
+
// Records a provider wait for the mailbox or the whole project, never shorter than the provider's Retry-After, and
|
|
242
|
+
// at least a day for the provider's daily limit, then throws it.
|
|
243
|
+
async defer(code, retryAfter, scope, daily) {
|
|
244
|
+
const now = this.now();
|
|
245
|
+
const retryAt = Math.max(sourceRetryAt(now, 1, retryAfter, 0), daily ? now + 86_400_000 : 0);
|
|
246
|
+
await this.options.quota.defer({ projectKey: this.options.projectKey,
|
|
247
|
+
...(scope === 'mailbox' ? { mailboxKey: this.options.mailboxKey } : {}) }, retryAt);
|
|
248
|
+
throw new SourceSyncFault(code, this.later(retryAt));
|
|
249
|
+
}
|
|
250
|
+
// A fresh access token from a flow built inside the grant operation and discarded with it. OAuth failures are
|
|
251
|
+
// translated inside the operation, whose boundary would turn an unknown error into a storage failure.
|
|
252
|
+
async accessToken(parent) {
|
|
253
|
+
active(parent);
|
|
254
|
+
return this.options.revocations.withGrant(this.options.currentHandle(), async (tokens, grantSignal) => {
|
|
255
|
+
return this.reserve('profile', async (requestSignal) => {
|
|
256
|
+
let retryAfter = null;
|
|
257
|
+
const observedFetch = async (input, init) => {
|
|
258
|
+
const response = await this.send(input, init);
|
|
259
|
+
retryAfter = response.headers.get('retry-after');
|
|
260
|
+
return response;
|
|
261
|
+
};
|
|
262
|
+
try {
|
|
263
|
+
const flow = new GmailOAuthFlow(this.options.client, tokens, {
|
|
264
|
+
signal: requestSignal, fetchImpl: observedFetch, endpoints: this.options.oauthEndpoints,
|
|
265
|
+
});
|
|
266
|
+
return await flow.accessToken(SOURCE_GRANT_KEY);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
// A flow stopped by the quota's or the grant's own deadline, not by the caller, was a network failure.
|
|
270
|
+
const translated = gmailOAuthFault(error);
|
|
271
|
+
const fault = translated.code === 'cancelled' && !parent?.aborted ? new SourceSyncFault('network') : translated;
|
|
272
|
+
if (fault.code === 'rate_limited' || fault.code === 'provider_unavailable') {
|
|
273
|
+
return this.defer(fault.code, retryAfter, 'mailbox', false);
|
|
274
|
+
}
|
|
275
|
+
throw fault;
|
|
276
|
+
}
|
|
277
|
+
}, grantSignal);
|
|
278
|
+
}, parent).catch((error) => {
|
|
279
|
+
// A grant lease that another worker holds answers its expiry, which can pass before this caller reads it.
|
|
280
|
+
if (error instanceof SourceSyncFault && error.retryAt !== null)
|
|
281
|
+
throw new SourceSyncFault(error.code, this.later(error.retryAt));
|
|
282
|
+
throw error;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/** Consume/cancel the whole body within the quota callback, before releasing its slot. */
|
|
286
|
+
async wire(url, token, quotaSignal, parent) {
|
|
287
|
+
const controller = new AbortController();
|
|
288
|
+
let timedOut = false;
|
|
289
|
+
const abort = () => controller.abort();
|
|
290
|
+
const stopped = () => new SourceSyncFault(parent?.aborted ? 'cancelled' : 'network');
|
|
291
|
+
quotaSignal.addEventListener('abort', abort, { once: true });
|
|
292
|
+
parent?.addEventListener('abort', abort, { once: true });
|
|
293
|
+
if (quotaSignal.aborted || parent?.aborted)
|
|
294
|
+
abort();
|
|
295
|
+
const timer = setTimeout(() => { timedOut = true; controller.abort(); }, this.limits.timeoutMs);
|
|
296
|
+
try {
|
|
297
|
+
if (controller.signal.aborted)
|
|
298
|
+
throw stopped();
|
|
299
|
+
const readStartedAt = this.now();
|
|
300
|
+
const response = await abortable(this.send(url, { method: 'GET',
|
|
301
|
+
headers: { accept: 'application/json', authorization: 'Bearer ' + token },
|
|
302
|
+
redirect: 'error', credentials: 'omit', cache: 'no-store', referrerPolicy: 'no-referrer', signal: controller.signal,
|
|
303
|
+
}), controller.signal, stopped, cancel);
|
|
304
|
+
if (controller.signal.aborted) {
|
|
305
|
+
cancel(response);
|
|
306
|
+
throw stopped();
|
|
307
|
+
}
|
|
308
|
+
if (response.status < 200 || response.status >= 600 || (response.status >= 300 && response.status < 400)) {
|
|
309
|
+
cancel(response);
|
|
310
|
+
return invalid();
|
|
311
|
+
}
|
|
312
|
+
let bytes;
|
|
313
|
+
try {
|
|
314
|
+
bytes = await bodyBytes(response, this.bounds.maxResponseBytes, controller.signal, stopped);
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
// Only a successful answer can be too large to use; an oversized failure is classified by its status alone.
|
|
318
|
+
if (error instanceof ReadCapExceeded) {
|
|
319
|
+
if (response.status === 200)
|
|
320
|
+
throw new GmailApiFault('response_too_large');
|
|
321
|
+
bytes = new Uint8Array(0);
|
|
322
|
+
}
|
|
323
|
+
else
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
if (controller.signal.aborted)
|
|
327
|
+
throw stopped();
|
|
328
|
+
const receivedAt = this.now();
|
|
329
|
+
if (receivedAt < readStartedAt)
|
|
330
|
+
return invalid();
|
|
331
|
+
let body = null;
|
|
332
|
+
try {
|
|
333
|
+
body = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
if (response.status === 200)
|
|
337
|
+
return invalid();
|
|
338
|
+
}
|
|
339
|
+
return { status: response.status, body, readStartedAt, receivedAt,
|
|
340
|
+
retryAfter: response.headers.get('retry-after'), cacheHeaders: {
|
|
341
|
+
cacheControl: response.headers.get('cache-control'), date: response.headers.get('date'),
|
|
342
|
+
age: response.headers.get('age'), expires: response.headers.get('expires'),
|
|
343
|
+
} };
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (parent?.aborted || timedOut || controller.signal.aborted)
|
|
347
|
+
throw stopped();
|
|
348
|
+
if (error instanceof SourceSyncFault || error instanceof GmailApiFault)
|
|
349
|
+
throw error;
|
|
350
|
+
throw new SourceSyncFault('network');
|
|
351
|
+
}
|
|
352
|
+
finally {
|
|
353
|
+
clearTimeout(timer);
|
|
354
|
+
quotaSignal.removeEventListener('abort', abort);
|
|
355
|
+
parent?.removeEventListener('abort', abort);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// One provider operation at a fixed path: a fresh token, one metered dispatch, and one repeat after a 401.
|
|
359
|
+
async request(method, path, query, signal) {
|
|
360
|
+
active(signal);
|
|
361
|
+
const expected = method === 'profile' ? 'profile' : method === 'labels' ? 'labels'
|
|
362
|
+
: method === 'list' ? 'threads' : method === 'changes' ? 'history' : null;
|
|
363
|
+
if ((expected !== null && path !== expected) || (expected === null && !/^threads\/[a-fA-F0-9]{1,64}$/.test(path)))
|
|
364
|
+
return invalid();
|
|
365
|
+
const url = new URL('/gmail/v1/users/me/' + path, this.origin);
|
|
366
|
+
for (const [key, value] of Object.entries(query))
|
|
367
|
+
url.searchParams.set(key, value);
|
|
368
|
+
for (let authAttempt = 0; authAttempt < 2; authAttempt++) {
|
|
369
|
+
const token = await this.accessToken(signal);
|
|
370
|
+
active(signal);
|
|
371
|
+
const response = await this.reserve(method, (quotaSignal) => this.wire(url, token, quotaSignal, signal), signal);
|
|
372
|
+
const disposition = classifyGmailHttp(response.status, response.body, method);
|
|
373
|
+
if (disposition.kind === 'ok')
|
|
374
|
+
return response;
|
|
375
|
+
if (disposition.kind === 'gone')
|
|
376
|
+
return null;
|
|
377
|
+
if (disposition.kind === 'refresh') {
|
|
378
|
+
if (authAttempt === 0)
|
|
379
|
+
continue;
|
|
380
|
+
throw new SourceSyncFault('grant_rejected');
|
|
381
|
+
}
|
|
382
|
+
if (disposition.kind === 'fault')
|
|
383
|
+
throw new SourceSyncFault(disposition.code);
|
|
384
|
+
if (disposition.kind === 'retry')
|
|
385
|
+
return this.defer(disposition.code, response.retryAfter, disposition.scope, disposition.daily);
|
|
386
|
+
}
|
|
387
|
+
return invalid();
|
|
388
|
+
}
|
|
389
|
+
// A control page: an absent or oversized answer fails without a partial page or a cursor advance.
|
|
390
|
+
async control(method, path, query, signal) {
|
|
391
|
+
try {
|
|
392
|
+
const result = await this.request(method, path, query, signal);
|
|
393
|
+
if (result === null)
|
|
394
|
+
return invalid();
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
if (error instanceof GmailApiFault && error.reason === 'response_too_large')
|
|
399
|
+
return invalid();
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* The mailbox address and current history id, for the host's private account binding and its history anchor. The
|
|
405
|
+
* answer's other fields are dropped.
|
|
406
|
+
*
|
|
407
|
+
* @param signal Cancels the call.
|
|
408
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for a malformed answer.
|
|
409
|
+
*/
|
|
410
|
+
async profile(signal) {
|
|
411
|
+
const response = await this.control('profile', 'profile', {}, signal);
|
|
412
|
+
const body = object(response.body);
|
|
413
|
+
if (typeof body.emailAddress !== 'string' || body.emailAddress.length > 320
|
|
414
|
+
|| !/^[^\s@<>]+@[^\s@<>]+$/.test(body.emailAddress) || !historyId(body.historyId))
|
|
415
|
+
return invalid();
|
|
416
|
+
return { emailAddress: body.emailAddress, historyId: body.historyId };
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* The mailbox's labels: sanitized names by id under the labels response's own cache decision. A refused decision
|
|
420
|
+
* keeps no names.
|
|
421
|
+
*
|
|
422
|
+
* @param permission The host's evidenced cache permission; null refuses the names.
|
|
423
|
+
* @param signal Cancels the call.
|
|
424
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for a malformed or oversized list.
|
|
425
|
+
*/
|
|
426
|
+
async labels(permission, signal) {
|
|
427
|
+
const response = await this.control('labels', 'labels', {}, signal);
|
|
428
|
+
const body = object(response.body);
|
|
429
|
+
const entries = array(body.labels === undefined ? [] : body.labels, this.limits.maxLabels);
|
|
430
|
+
const cache = decideSourceCache({ permission, maximumCacheMs: this.bounds.maximumCacheMs,
|
|
431
|
+
readStartedAt: response.readStartedAt, receivedAt: response.receivedAt,
|
|
432
|
+
windowExpiresAt: response.readStartedAt + this.bounds.maximumCacheMs, headers: response.cacheHeaders });
|
|
433
|
+
const names = Object.create(null);
|
|
434
|
+
const seen = new Set();
|
|
435
|
+
for (const entry of entries) {
|
|
436
|
+
const row = object(entry);
|
|
437
|
+
if (!labelId(row.id) || seen.has(row.id) || typeof row.name !== 'string'
|
|
438
|
+
|| row.name.length > this.bounds.maxInputCharacters)
|
|
439
|
+
return invalid();
|
|
440
|
+
seen.add(row.id);
|
|
441
|
+
if (cache.kind === 'cache')
|
|
442
|
+
names[row.id] = sanitizeMailMetadata(row.name, {
|
|
443
|
+
maxInputCharacters: this.bounds.maxInputCharacters, maxLineCharacters: this.bounds.maxLineCharacters,
|
|
444
|
+
maxOutputCharacters: Math.min(200, this.bounds.maxInputCharacters),
|
|
445
|
+
}).text;
|
|
446
|
+
}
|
|
447
|
+
active(signal);
|
|
448
|
+
return { names: Object.freeze(names), cache };
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* One page of candidate thread ids between `cutoff - windowMs` and `cutoff`, outside spam, trash and drafts. The
|
|
452
|
+
* query's whole-second bounds are widened by a second each way; the mapper enforces the exact window. Snippets and
|
|
453
|
+
* estimates are dropped, and no mailbox-wide completeness is claimed.
|
|
454
|
+
*
|
|
455
|
+
* @param cutoff The selection cutoff in epoch milliseconds.
|
|
456
|
+
* @param windowMs The window, from 1 millisecond to 90 days.
|
|
457
|
+
* @param pageSize Candidates per page, from 1 to 100.
|
|
458
|
+
* @param pageToken The resume token from the previous page, or null for the first.
|
|
459
|
+
* @param signal Cancels the call.
|
|
460
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for bad arguments, a malformed page or a repeated
|
|
461
|
+
* page token.
|
|
462
|
+
*/
|
|
463
|
+
async list(cutoff, windowMs, pageSize, pageToken, signal) {
|
|
464
|
+
this.pageSize(pageSize);
|
|
465
|
+
const token = this.pageToken(pageToken);
|
|
466
|
+
if (!integer(cutoff) || !integer(windowMs) || windowMs < 1 || windowMs > 90 * 86_400_000)
|
|
467
|
+
return invalid();
|
|
468
|
+
const after = Math.max(0, Math.floor((cutoff - windowMs) / 1000) - 1);
|
|
469
|
+
const before = Math.floor(cutoff / 1000) + 1;
|
|
470
|
+
const response = await this.control('list', 'threads', { maxResults: String(pageSize), includeSpamTrash: 'false',
|
|
471
|
+
q: `after:${after} before:${before} -in:spam -in:trash -in:drafts`,
|
|
472
|
+
...(token === null ? {} : { pageToken: token }),
|
|
473
|
+
}, signal);
|
|
474
|
+
const body = object(response.body);
|
|
475
|
+
const entries = array(body.threads === undefined ? [] : body.threads, pageSize);
|
|
476
|
+
const ids = entries.map((entry) => { const id = object(entry).id; if (!gmailId(id))
|
|
477
|
+
return invalid(); return id; });
|
|
478
|
+
const nextPageToken = this.pageToken(body.nextPageToken);
|
|
479
|
+
if (nextPageToken !== null && nextPageToken === token)
|
|
480
|
+
return invalid();
|
|
481
|
+
return { kind: 'scan', ids, nextPageToken };
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* One page of history after `fromCursor`: the thread ids of every message addition, deletion and label change, and
|
|
485
|
+
* of generic message entries, deduplicated and sorted, with the page's decimal-string cursor. The host persists the
|
|
486
|
+
* pending work and commits a terminal cursor only once every item is terminal.
|
|
487
|
+
*
|
|
488
|
+
* @param fromCursor The committed history id, a decimal string.
|
|
489
|
+
* @param pageSize Records per page, from 1 to 100.
|
|
490
|
+
* @param pageToken The resume token from the previous page, or null for the first.
|
|
491
|
+
* @param signal Cancels the call.
|
|
492
|
+
* @throws {SourceSyncFault} `history_expired` when Google no longer holds the anchor; `invalid_response` for bad
|
|
493
|
+
* arguments, a malformed or out-of-order page, too many references or a repeated page token; another fixed failure.
|
|
494
|
+
*/
|
|
495
|
+
async history(fromCursor, pageSize, pageToken, signal) {
|
|
496
|
+
this.pageSize(pageSize);
|
|
497
|
+
const token = this.pageToken(pageToken);
|
|
498
|
+
if (!historyId(fromCursor))
|
|
499
|
+
return invalid();
|
|
500
|
+
const response = await this.control('changes', 'history', { startHistoryId: fromCursor, maxResults: String(pageSize),
|
|
501
|
+
...(token === null ? {} : { pageToken: token }),
|
|
502
|
+
}, signal);
|
|
503
|
+
const body = object(response.body);
|
|
504
|
+
if (!historyId(body.historyId) || compareHistory(body.historyId, fromCursor) < 0)
|
|
505
|
+
return invalid();
|
|
506
|
+
const records = array(body.history === undefined ? [] : body.history, pageSize);
|
|
507
|
+
const ids = new Set();
|
|
508
|
+
let references = 0;
|
|
509
|
+
let previous = fromCursor;
|
|
510
|
+
const changed = (value) => {
|
|
511
|
+
if (++references > this.limits.maxHistoryReferences)
|
|
512
|
+
return invalid();
|
|
513
|
+
const message = object(value);
|
|
514
|
+
if (!gmailId(message.id) || !gmailId(message.threadId))
|
|
515
|
+
return invalid();
|
|
516
|
+
ids.add(message.threadId);
|
|
517
|
+
};
|
|
518
|
+
for (const value of records) {
|
|
519
|
+
const entry = object(value);
|
|
520
|
+
if (!historyId(entry.id) || compareHistory(entry.id, fromCursor) <= 0
|
|
521
|
+
|| compareHistory(entry.id, previous) < 0 || compareHistory(entry.id, body.historyId) > 0)
|
|
522
|
+
return invalid();
|
|
523
|
+
previous = entry.id;
|
|
524
|
+
for (const field of ['messages', 'messagesAdded', 'messagesDeleted', 'labelsAdded', 'labelsRemoved']) {
|
|
525
|
+
const events = array(entry[field] === undefined ? [] : entry[field], this.limits.maxHistoryReferences - references);
|
|
526
|
+
for (const event of events)
|
|
527
|
+
changed(field === 'messages' ? event : object(event).message);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const nextPageToken = this.pageToken(body.nextPageToken);
|
|
531
|
+
if (nextPageToken !== null && nextPageToken === token)
|
|
532
|
+
return invalid();
|
|
533
|
+
return { kind: 'history', ids: [...ids].sort(), nextPageToken, cursor: body.historyId };
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Reads one thread in the `full` format and maps it with its own read times and cache headers and the separate
|
|
537
|
+
* label snapshot. A 404 is `gone`; an oversized successful answer is skipped as `response_too_large`; any other
|
|
538
|
+
* result is the mapper's. The raw response never leaves the client.
|
|
539
|
+
*
|
|
540
|
+
* @param sourceId The thread id, 1 to 64 hexadecimal characters.
|
|
541
|
+
* @param options The cutoff, window, detail path, cache permission, label snapshot and signal.
|
|
542
|
+
* @throws {SourceSyncFault} A fixed failure, `invalid_response` for a malformed id.
|
|
543
|
+
*/
|
|
544
|
+
async readThread(sourceId, options) {
|
|
545
|
+
if (!gmailId(sourceId))
|
|
546
|
+
return invalid();
|
|
547
|
+
active(options.signal);
|
|
548
|
+
let response;
|
|
549
|
+
try {
|
|
550
|
+
response = await this.request('read', 'threads/' + sourceId, { format: 'full' }, options.signal);
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
if (error instanceof GmailApiFault && error.reason === 'response_too_large') {
|
|
554
|
+
return { kind: 'skipped', id: sourceId, reason: 'response_too_large' };
|
|
555
|
+
}
|
|
556
|
+
throw error;
|
|
557
|
+
}
|
|
558
|
+
if (response === null)
|
|
559
|
+
return { kind: 'gone', id: sourceId };
|
|
560
|
+
return this.mapper.map(response.body, {
|
|
561
|
+
sourceId, sourceDetailUrl: options.sourceDetailUrl, cutoff: options.cutoff, windowMs: options.windowMs,
|
|
562
|
+
readStartedAt: response.readStartedAt, receivedAt: response.receivedAt,
|
|
563
|
+
permission: options.permission, cacheHeaders: response.cacheHeaders,
|
|
564
|
+
labelNames: options.labels?.names ?? {}, labelCache: options.labels?.cache ?? null, signal: options.signal,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
//# sourceMappingURL=gmail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmail.js","sourceRoot":"","sources":["../src/gmail.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* @fileoverview A read-only Gmail source's grant: Google's read-only scope on the AgentOS redirect OAuth flow and
|
|
3
3
|
* sealed-token store, with capped, cancellable OAuth calls and fixed failures; its text helpers: bounded
|
|
4
4
|
* pre-storage sanitization of mail text, allow-listed headers and metadata, and conservative trimming of reply history
|
|
5
|
-
* and signatures;
|
|
6
|
-
* with message provenance, attachment listings and an explicit cache decision
|
|
5
|
+
* and signatures; its thread mapper: bounded MIME reading of one thread into a sanitized account-source document
|
|
6
|
+
* with message provenance, attachment listings and an explicit cache decision; and its read-only API client: capped
|
|
7
|
+
* profile, label, list, history and thread calls through the host's grant and quota services, with fixed failures.
|
|
7
8
|
* @module agentos-ext-gmail-source
|
|
8
9
|
*/
|
|
9
10
|
export { GmailOAuthFlow } from './GmailOAuthFlow.js';
|
|
@@ -12,4 +13,6 @@ export { GmailTextFault, sanitizeMailText, sanitizeMailMetadata, sanitizeMailHea
|
|
|
12
13
|
export { messageText, type GmailMessageText } from './messageText.js';
|
|
13
14
|
export { GmailThreadMapper, type GmailMapThreadOptions } from './mapThread.js';
|
|
14
15
|
export { GmailMappingFault, type GmailMappingBounds } from './mime.js';
|
|
16
|
+
export { GmailApi, GmailApiFault, type GmailApiOptions, type GmailApiLimits, type GmailProfile, type GmailLabels, type GmailReadOptions, } from './gmail.js';
|
|
17
|
+
export { classifyGmailHttp, gmailOAuthFault, type GmailHttpDisposition } from './retry.js';
|
|
15
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,oBAAoB,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EACrF,KAAK,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,GACxE,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAC3E,KAAK,eAAe,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EACpE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,GAClD,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,QAAQ,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAClE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,GAC3D,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* @fileoverview A read-only Gmail source's grant: Google's read-only scope on the AgentOS redirect OAuth flow and
|
|
3
3
|
* sealed-token store, with capped, cancellable OAuth calls and fixed failures; its text helpers: bounded
|
|
4
4
|
* pre-storage sanitization of mail text, allow-listed headers and metadata, and conservative trimming of reply history
|
|
5
|
-
* and signatures;
|
|
6
|
-
* with message provenance, attachment listings and an explicit cache decision
|
|
5
|
+
* and signatures; its thread mapper: bounded MIME reading of one thread into a sanitized account-source document
|
|
6
|
+
* with message provenance, attachment listings and an explicit cache decision; and its read-only API client: capped
|
|
7
|
+
* profile, label, list, history and thread calls through the host's grant and quota services, with fixed failures.
|
|
7
8
|
* @module agentos-ext-gmail-source
|
|
8
9
|
*/
|
|
9
10
|
export { GmailOAuthFlow } from './GmailOAuthFlow.js';
|
|
@@ -12,4 +13,6 @@ export { GmailTextFault, sanitizeMailText, sanitizeMailMetadata, sanitizeMailHea
|
|
|
12
13
|
export { messageText } from './messageText.js';
|
|
13
14
|
export { GmailThreadMapper } from './mapThread.js';
|
|
14
15
|
export { GmailMappingFault } from './mime.js';
|
|
16
|
+
export { GmailApi, GmailApiFault, } from './gmail.js';
|
|
17
|
+
export { classifyGmailHttp, gmailOAuthFault } from './retry.js';
|
|
15
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACL,oBAAoB,EAAE,eAAe,GAEtC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,GAG5E,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAA8B,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAA2B,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,QAAQ,EAAE,aAAa,GAExB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAA6B,MAAM,YAAY,CAAC"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SourceSyncFault, type SourceQuotaMethod, type SourceSyncFailureCode } from '@framers/agentos-ext-account-sources';
|
|
2
|
+
/**
|
|
3
|
+
* What one Gmail API answer calls for: `ok` for a success; `refresh` for a 401, which earns one fresh access token and
|
|
4
|
+
* one repeat of the same call; `gone` for a canonical thread read's 404; `retry` for a rate limit or a server failure,
|
|
5
|
+
* with the scope its wait applies to and whether it is the provider's daily limit; `fault` for a fixed failure.
|
|
6
|
+
*/
|
|
7
|
+
export type GmailHttpDisposition = {
|
|
8
|
+
readonly kind: 'ok' | 'refresh' | 'gone';
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: 'retry';
|
|
11
|
+
readonly code: 'rate_limited' | 'provider_unavailable';
|
|
12
|
+
readonly scope: 'project' | 'mailbox';
|
|
13
|
+
readonly daily: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: 'fault';
|
|
16
|
+
readonly code: SourceSyncFailureCode;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Classifies one Gmail API answer by its status and, for a 403, the exact reason tokens of at most 32 error entries.
|
|
20
|
+
* A 429 or 5xx is retried whether or not its body parses. A 403 `domainPolicy` wins over any other reason, then
|
|
21
|
+
* `insufficientPermissions`, `dailyLimitExceeded` (the whole project, daily), `rateLimitExceeded` (the whole project)
|
|
22
|
+
* and `userRateLimitExceeded` (the mailbox). A 404 is `gone` for a thread read and `history_expired` for a history
|
|
23
|
+
* page. Any other answer, an unknown reason included, is the fixed `invalid_response`, never an inferred grant loss.
|
|
24
|
+
* Only exact reason tokens influence policy. Provider text never leaves this function.
|
|
25
|
+
*
|
|
26
|
+
* @param status The HTTP status.
|
|
27
|
+
* @param body The answer's parsed JSON, or null when it did not parse.
|
|
28
|
+
* @param method The kind of call that was answered.
|
|
29
|
+
*/
|
|
30
|
+
export declare function classifyGmailHttp(status: number, body: unknown, method: SourceQuotaMethod): GmailHttpDisposition;
|
|
31
|
+
/**
|
|
32
|
+
* Translates a failure of an OAuth grant operation into a fixed account-source fault. A missing grant or Google's
|
|
33
|
+
* `invalid_grant` is `grant_rejected` and `invalid_scope` or a scope check is `insufficient_scope`, the two that start
|
|
34
|
+
* the coordinator's fenced grant-loss lifecycle; a 429 is `rate_limited`; a 5xx, `server_error` or
|
|
35
|
+
* `temporarily_unavailable` is `provider_unavailable`; a transport failure or deadline is `network`. A refused client
|
|
36
|
+
* (`invalid_client`, `unauthorized_client`) needs an operator's configuration fix and, like every other failure, is
|
|
37
|
+
* `invalid_response`: never a revoked mailbox. A `SourceSyncFault` is returned unchanged.
|
|
38
|
+
*
|
|
39
|
+
* Call inside withGrant so that its fixed-fault boundary preserves this distinction.
|
|
40
|
+
*
|
|
41
|
+
* @param error What the grant operation threw.
|
|
42
|
+
*/
|
|
43
|
+
export declare function gmailOAuthFault(error: unknown): SourceSyncFault;
|
|
44
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,eAAe,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EACpE,MAAM,sCAAsC,CAAC;AAG9C;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,CAAA;CAAE,GAC5C;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,sBAAsB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAClJ;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAMrE;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,GAAG,oBAAoB,CA0BhH;AACD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAiB/D"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fixed classification of Gmail API answers and OAuth refresh failures. Only an HTTP status and exact,
|
|
3
|
+
* allow-listed reason tokens decide a disposition; provider text, error bodies and causes never reach a result or an
|
|
4
|
+
* error. The durable account-source coordinator owns retry episodes, backoff and grant-loss handling.
|
|
5
|
+
* @module agentos-ext-gmail-source/retry
|
|
6
|
+
*/
|
|
7
|
+
import { OAuthGrantRefused } from '@framers/agentos/auth';
|
|
8
|
+
import { SourceSyncFault, } from '@framers/agentos-ext-account-sources';
|
|
9
|
+
import { GmailOAuthFault } from './types.js';
|
|
10
|
+
// A plain object, or null for anything else.
|
|
11
|
+
function record(value) {
|
|
12
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
13
|
+
? value : null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Classifies one Gmail API answer by its status and, for a 403, the exact reason tokens of at most 32 error entries.
|
|
17
|
+
* A 429 or 5xx is retried whether or not its body parses. A 403 `domainPolicy` wins over any other reason, then
|
|
18
|
+
* `insufficientPermissions`, `dailyLimitExceeded` (the whole project, daily), `rateLimitExceeded` (the whole project)
|
|
19
|
+
* and `userRateLimitExceeded` (the mailbox). A 404 is `gone` for a thread read and `history_expired` for a history
|
|
20
|
+
* page. Any other answer, an unknown reason included, is the fixed `invalid_response`, never an inferred grant loss.
|
|
21
|
+
* Only exact reason tokens influence policy. Provider text never leaves this function.
|
|
22
|
+
*
|
|
23
|
+
* @param status The HTTP status.
|
|
24
|
+
* @param body The answer's parsed JSON, or null when it did not parse.
|
|
25
|
+
* @param method The kind of call that was answered.
|
|
26
|
+
*/
|
|
27
|
+
export function classifyGmailHttp(status, body, method) {
|
|
28
|
+
if (status === 200)
|
|
29
|
+
return { kind: 'ok' };
|
|
30
|
+
if (status === 401)
|
|
31
|
+
return { kind: 'refresh' };
|
|
32
|
+
if (status === 404 && method === 'read')
|
|
33
|
+
return { kind: 'gone' };
|
|
34
|
+
if (status === 404 && method === 'changes')
|
|
35
|
+
return { kind: 'fault', code: 'history_expired' };
|
|
36
|
+
const retry = (code, scope, daily = false) => ({ kind: 'retry', code, scope, daily });
|
|
37
|
+
if (status === 429)
|
|
38
|
+
return retry('rate_limited', 'mailbox');
|
|
39
|
+
if (status >= 500 && status <= 599)
|
|
40
|
+
return retry('provider_unavailable', 'mailbox');
|
|
41
|
+
if (status === 403) {
|
|
42
|
+
const entries = record(record(body)?.error)?.errors;
|
|
43
|
+
const reasons = new Set();
|
|
44
|
+
if (Array.isArray(entries) && entries.length <= 32) {
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
const reason = record(entry)?.reason;
|
|
47
|
+
if (typeof reason === 'string' && ['domainPolicy', 'insufficientPermissions', 'dailyLimitExceeded',
|
|
48
|
+
'rateLimitExceeded', 'userRateLimitExceeded'].includes(reason))
|
|
49
|
+
reasons.add(reason);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (reasons.has('domainPolicy'))
|
|
53
|
+
return { kind: 'fault', code: 'domain_policy' };
|
|
54
|
+
if (reasons.has('insufficientPermissions'))
|
|
55
|
+
return { kind: 'fault', code: 'insufficient_scope' };
|
|
56
|
+
if (reasons.has('dailyLimitExceeded'))
|
|
57
|
+
return retry('rate_limited', 'project', true);
|
|
58
|
+
if (reasons.has('rateLimitExceeded'))
|
|
59
|
+
return retry('rate_limited', 'project');
|
|
60
|
+
if (reasons.has('userRateLimitExceeded'))
|
|
61
|
+
return retry('rate_limited', 'mailbox');
|
|
62
|
+
}
|
|
63
|
+
return { kind: 'fault', code: 'invalid_response' };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Translates a failure of an OAuth grant operation into a fixed account-source fault. A missing grant or Google's
|
|
67
|
+
* `invalid_grant` is `grant_rejected` and `invalid_scope` or a scope check is `insufficient_scope`, the two that start
|
|
68
|
+
* the coordinator's fenced grant-loss lifecycle; a 429 is `rate_limited`; a 5xx, `server_error` or
|
|
69
|
+
* `temporarily_unavailable` is `provider_unavailable`; a transport failure or deadline is `network`. A refused client
|
|
70
|
+
* (`invalid_client`, `unauthorized_client`) needs an operator's configuration fix and, like every other failure, is
|
|
71
|
+
* `invalid_response`: never a revoked mailbox. A `SourceSyncFault` is returned unchanged.
|
|
72
|
+
*
|
|
73
|
+
* Call inside withGrant so that its fixed-fault boundary preserves this distinction.
|
|
74
|
+
*
|
|
75
|
+
* @param error What the grant operation threw.
|
|
76
|
+
*/
|
|
77
|
+
export function gmailOAuthFault(error) {
|
|
78
|
+
if (error instanceof SourceSyncFault)
|
|
79
|
+
return error;
|
|
80
|
+
if (error instanceof GmailOAuthFault) {
|
|
81
|
+
if (error.reason === 'scope')
|
|
82
|
+
return new SourceSyncFault('insufficient_scope');
|
|
83
|
+
if (error.reason === 'network' || error.reason === 'timeout')
|
|
84
|
+
return new SourceSyncFault('network');
|
|
85
|
+
if (error.reason === 'cancelled')
|
|
86
|
+
return new SourceSyncFault('cancelled');
|
|
87
|
+
return new SourceSyncFault('invalid_response');
|
|
88
|
+
}
|
|
89
|
+
if (error instanceof OAuthGrantRefused) {
|
|
90
|
+
if (error.reason === 'missing' || error.providerError === 'invalid_grant')
|
|
91
|
+
return new SourceSyncFault('grant_rejected');
|
|
92
|
+
if (error.providerError === 'invalid_scope')
|
|
93
|
+
return new SourceSyncFault('insufficient_scope');
|
|
94
|
+
if (error.status === 429)
|
|
95
|
+
return new SourceSyncFault('rate_limited');
|
|
96
|
+
if ((error.status !== undefined && error.status >= 500) || ['server_error', 'temporarily_unavailable'].includes(error.providerError ?? '')) {
|
|
97
|
+
return new SourceSyncFault('provider_unavailable');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return new SourceSyncFault('invalid_response');
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"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"}
|
package/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://agentos.sh/schemas/extension-manifest-v1.json",
|
|
3
3
|
"id": "com.framers.productivity.gmail-source",
|
|
4
4
|
"name": "Gmail Source",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.0",
|
|
6
6
|
"description": "Read-only OAuth and bounded personal Gmail source helpers",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Frame",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@framers/agentos-ext-gmail-source",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Read-only OAuth and bounded personal Gmail source helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"typescript": "5.4.5",
|
|
53
53
|
"vitest": "^3.2.7",
|
|
54
54
|
"@framers/agentos": "^0.13.134",
|
|
55
|
-
"@framers/agentos-ext-account-sources": "^0.7.
|
|
55
|
+
"@framers/agentos-ext-account-sources": "^0.7.1"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsc",
|