@wirecat/cli-core 0.545.0 → 0.547.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/CHANGELOG.md +228 -0
- package/dist/calendar/busy.js +4 -1
- package/dist/calendar/cli/answers.d.ts +31 -0
- package/dist/calendar/cli/answers.js +8 -0
- package/dist/calendar/cli/index.d.ts +6 -0
- package/dist/calendar/cli/index.js +43 -4
- package/dist/calendar/cli/remind.js +2 -1
- package/dist/calendar/expand.d.ts +17 -0
- package/dist/calendar/expand.js +340 -0
- package/dist/calendar/feeds.d.ts +96 -0
- package/dist/calendar/feeds.js +324 -0
- package/dist/calendar/ics.d.ts +15 -4
- package/dist/calendar/ics.js +18 -7
- package/dist/calendar/index.d.ts +3 -1
- package/dist/calendar/index.js +2 -0
- package/dist/calendar/server-search.js +3 -1
- package/dist/calendar/writes.js +15 -4
- package/dist/cli/after-dashes.d.ts +16 -0
- package/dist/cli/after-dashes.js +41 -0
- package/dist/cli/bot/context.d.ts +1 -1
- package/dist/cli/bot/context.js +9 -1
- package/dist/cli/bot/messages.js +2 -0
- package/dist/cli/bot/setup.js +1 -0
- package/dist/cli/contract-templates.js +2 -2
- package/dist/cli/entry.d.ts +2 -0
- package/dist/cli/entry.js +3 -2
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/messenger/admin-commands.js +5 -1
- package/dist/cli/messenger/admin-contacts-command.js +5 -1
- package/dist/cli/messenger/admin-folders-command.js +5 -1
- package/dist/cli/messenger/complete-command.js +1 -0
- package/dist/cli/messenger/history-query.d.ts +10 -4
- package/dist/cli/messenger/history-query.js +87 -31
- package/dist/cli/messenger/messages-command.js +12 -23
- package/dist/cli/messenger/messages-comments-command.js +4 -2
- package/dist/cli/messenger/messages-edit-command.js +1 -0
- package/dist/cli/messenger/messages-press-command.js +1 -0
- package/dist/cli/messenger/messages-send-command.js +1 -0
- package/dist/cli/messenger/observed.js +2 -2
- package/dist/cli/messenger/polls-command.js +1 -0
- package/dist/cli/messenger/private-people-command.js +5 -1
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/messenger/stored.js +56 -3
- package/dist/cli/messenger/topics-command.js +1 -1
- package/dist/cli/option-hints.d.ts +6 -0
- package/dist/cli/option-hints.js +15 -0
- package/dist/cli/program.js +17 -6
- package/dist/cli/schedule/schedule-command.js +1 -0
- package/dist/domain/message-link.d.ts +3 -0
- package/dist/domain/message-link.js +9 -14
- package/dist/domain/money-words.d.ts +11 -0
- package/dist/domain/money-words.js +38 -0
- package/dist/domain/providers.d.ts +2 -0
- package/dist/domain/providers.js +15 -3
- package/dist/domain/ref-arguments.d.ts +31 -0
- package/dist/domain/ref-arguments.js +79 -0
- package/dist/domain/references.d.ts +20 -2
- package/dist/domain/references.js +132 -47
- package/dist/errors/reasons.js +31 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mail/cli/index.js +4 -4
- package/dist/mail/index.d.ts +2 -0
- package/dist/mail/index.js +2 -0
- package/dist/mail/partial.d.ts +34 -0
- package/dist/mail/partial.js +94 -0
- package/dist/mail/server-search.js +17 -9
- package/dist/mail/source.d.ts +13 -2
- package/dist/mail/source.js +7 -1
- package/dist/mail/sync-types.d.ts +112 -0
- package/dist/mail/sync-types.js +1 -0
- package/dist/mail/sync.d.ts +4 -106
- package/dist/mail/sync.js +49 -4
- package/dist/mail/testing/fake.d.ts +3 -1
- package/dist/mail/testing/fake.js +10 -2
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/oauth/client-file.d.ts +14 -0
- package/dist/oauth/client-file.js +28 -0
- package/dist/oauth/index.d.ts +1 -0
- package/dist/oauth/index.js +1 -0
- package/dist/ports/messenger.d.ts +3 -2
- package/dist/resolve.d.ts +3 -1
- package/dist/resolve.js +8 -0
- package/dist/search/task-signature.d.ts +5 -1
- package/dist/search/task-signature.js +27 -9
- package/dist/services/agenda.d.ts +5 -0
- package/dist/services/agenda.js +3 -1
- package/dist/services/deps.d.ts +0 -2
- package/dist/services/deps.js +0 -2
- package/dist/services/documents/ingestion.js +1 -1
- package/dist/services/documents/knowledge.d.ts +10 -6
- package/dist/services/documents/knowledge.js +25 -11
- package/dist/services/index.d.ts +6 -1
- package/dist/services/index.js +6 -1
- package/dist/services/message-resolution.d.ts +4 -1
- package/dist/services/message-resolution.js +19 -3
- package/dist/services/messages.d.ts +2 -0
- package/dist/services/messages.js +40 -36
- package/dist/services/money-words.d.ts +3 -0
- package/dist/services/money-words.js +5 -0
- package/dist/services/proposal-tasks.d.ts +3 -2
- package/dist/services/proposal-tasks.js +5 -2
- package/dist/services/proposals/ids.d.ts +5 -0
- package/dist/services/proposals/ids.js +43 -0
- package/dist/services/proposals/index.d.ts +3 -0
- package/dist/services/proposals/index.js +2 -0
- package/dist/services/proposals/kinds/action.d.ts +7 -0
- package/dist/services/proposals/kinds/action.js +45 -0
- package/dist/services/proposals/kinds/fact.d.ts +4 -0
- package/dist/services/proposals/kinds/fact.js +43 -0
- package/dist/services/proposals/kinds/findings.d.ts +7 -0
- package/dist/services/proposals/kinds/findings.js +75 -0
- package/dist/services/proposals/kinds/key-document.d.ts +9 -0
- package/dist/services/proposals/kinds/key-document.js +75 -0
- package/dist/services/proposals/kinds/memory.d.ts +4 -0
- package/dist/services/proposals/kinds/memory.js +42 -0
- package/dist/services/proposals/kinds/relationship.d.ts +10 -0
- package/dist/services/proposals/kinds/relationship.js +82 -0
- package/dist/services/proposals/kinds/shared.d.ts +25 -0
- package/dist/services/proposals/kinds/shared.js +49 -0
- package/dist/services/proposals/registry.d.ts +9 -0
- package/dist/services/proposals/registry.js +129 -0
- package/dist/services/proposals/types.d.ts +85 -0
- package/dist/services/proposals/types.js +1 -0
- package/dist/services/questions.d.ts +7 -2
- package/dist/services/questions.js +13 -4
- package/dist/services/server-search.d.ts +7 -4
- package/dist/services/server-search.js +5 -2
- package/dist/services/stored-project-gathering.d.ts +9 -0
- package/dist/services/stored-project-gathering.js +9 -4
- package/dist/services/task-categories.d.ts +2 -0
- package/dist/services/task-categories.js +19 -8
- package/dist/services/tasks.js +8 -4
- package/dist/store/decisions.d.ts +65 -0
- package/dist/store/decisions.js +55 -0
- package/dist/store/evidence-findings.d.ts +6 -6
- package/dist/store/index.d.ts +4 -3
- package/dist/store/index.js +2 -1
- package/dist/store/meeting-findings.d.ts +2 -0
- package/dist/store/messages.d.ts +4 -9
- package/dist/store/sqlite/actors.d.ts +13 -2
- package/dist/store/sqlite/actors.js +24 -1
- package/dist/store/sqlite/email-locations.d.ts +16 -0
- package/dist/store/sqlite/email-locations.js +26 -0
- package/dist/store/sqlite/emails.d.ts +6 -2
- package/dist/store/sqlite/emails.js +2 -1
- package/dist/store/sqlite/evidence-findings.js +13 -3
- package/dist/store/sqlite/evidence-sources.js +2 -2
- package/dist/store/sqlite/facts.d.ts +16 -8
- package/dist/store/sqlite/facts.js +48 -15
- package/dist/store/sqlite/findings-core.d.ts +2 -1
- package/dist/store/sqlite/findings-core.js +4 -4
- package/dist/store/sqlite/identities.js +1 -1
- package/dist/store/sqlite/knowledge.d.ts +43 -7
- package/dist/store/sqlite/knowledge.js +62 -17
- package/dist/store/sqlite/meeting-findings.js +6 -6
- package/dist/store/sqlite/memories.d.ts +12 -8
- package/dist/store/sqlite/memories.js +33 -10
- package/dist/store/sqlite/notes.d.ts +5 -1
- package/dist/store/sqlite/notes.js +22 -6
- package/dist/store/sqlite/open-store.d.ts +4 -1
- package/dist/store/sqlite/open-store.js +8 -2
- package/dist/store/sqlite/open.d.ts +3 -0
- package/dist/store/sqlite/proposed-actions.d.ts +21 -8
- package/dist/store/sqlite/proposed-actions.js +29 -9
- package/dist/store/sqlite/reads.d.ts +13 -2
- package/dist/store/sqlite/reads.js +8 -4
- package/dist/store/sqlite/relation-proposals.d.ts +34 -0
- package/dist/store/sqlite/relation-proposals.js +114 -0
- package/dist/store/sqlite/things.d.ts +0 -1
- package/dist/store/sqlite/things.js +25 -24
- package/dist/store/store.d.ts +3 -0
- package/dist/store/store.js +2 -0
- package/package.json +1 -1
package/dist/errors/reasons.js
CHANGED
|
@@ -29,6 +29,8 @@ const SIGN_IN = [
|
|
|
29
29
|
["oauth.revoke-failed", "outcome_unknown", "the provider did not confirm the revoke"],
|
|
30
30
|
["oauth.invalid", "authentication_error", "the saved sign-in cannot be read"],
|
|
31
31
|
["oauth.not-saved", "configuration_error", "the sign-in cannot be saved or read where it is kept"],
|
|
32
|
+
["sign-in.client-file-open", "configuration_error", "other users of this computer can read the client file"],
|
|
33
|
+
["sign-in.client-file-invalid", "configuration_error", "the file is not a Desktop app OAuth client JSON"],
|
|
32
34
|
];
|
|
33
35
|
const OTHERS = [
|
|
34
36
|
["model.offline", "permission_error", "downloading a model needs the network; --offline was requested"],
|
|
@@ -78,6 +80,17 @@ const OTHERS = [
|
|
|
78
80
|
validation("ics.too-many", "the file holds more events than one import sends"),
|
|
79
81
|
validation("ics.too-large", "the file is larger than one import reads"),
|
|
80
82
|
["calendar.read-only", "permission_error", "the calendar takes no new events from this account"],
|
|
83
|
+
validation("feed.address-invalid", "a feed address is an http, https or webcal URL with no user or password in it"),
|
|
84
|
+
validation("feed.duplicate", "this profile follows a feed at this address already"),
|
|
85
|
+
validation("feed.not-a-feed", "the calendar is not a feed this profile follows"),
|
|
86
|
+
validation("feed.too-large", "the feed is larger than one sync reads"),
|
|
87
|
+
validation("feed.redirect-refused", "the feed sent the request on to an address that is not https"),
|
|
88
|
+
["feed.no-address", "configuration_error", "the feed's address is no longer kept on this computer"],
|
|
89
|
+
["feed.refused", "provider_error", "the feed's server refused the request"],
|
|
90
|
+
["feed.gone", "not_found", "the feed's server has no feed at this address any more"],
|
|
91
|
+
["feed.timeout", "timeout", "the feed's server did not answer in time"],
|
|
92
|
+
["feed.unreachable", "network_error", "the feed's server could not be reached"],
|
|
93
|
+
validation("busy.feed", "a feed calendar has no busy times to ask; read its events with events list"),
|
|
81
94
|
["calendar.unsynced", "not_found", "the calendar account has no synced calendar in the store"],
|
|
82
95
|
["calendar.sync-incomplete", "not_found", "a synced calendar's sync never finished or is mid-round"],
|
|
83
96
|
["calendar.no-upcoming", "not_found", "no upcoming meeting with other attendees is stored for this account"],
|
|
@@ -97,7 +110,11 @@ const OTHERS = [
|
|
|
97
110
|
["search.server-failed", "provider_unavailable", "the server did not answer a search"],
|
|
98
111
|
["search.server-timeout", "timeout", "the server did not answer within --server-time"],
|
|
99
112
|
["search.server-capped", "outcome_unknown", "the server found more than one search reads, or a branch was not sent"],
|
|
100
|
-
[
|
|
113
|
+
[
|
|
114
|
+
"search.server-partial",
|
|
115
|
+
"outcome_unknown",
|
|
116
|
+
"a message the server found was stored without its attachments; the next sync reads it whole",
|
|
117
|
+
],
|
|
101
118
|
validation("store.refused", "the local store refused the document text"),
|
|
102
119
|
["store.not-writable", "permission_error", "the message store cannot be written from this process"],
|
|
103
120
|
[
|
|
@@ -132,7 +149,20 @@ const OTHERS = [
|
|
|
132
149
|
validation("name.taken", "the name is already used"),
|
|
133
150
|
validation("period.ambiguous", "the period can be read more than one way"),
|
|
134
151
|
validation("proposal.wrong-state", "the proposal is not in a state that allows this"),
|
|
152
|
+
validation("proposal.reason-required", "an agent deciding for the owner gives a reason"),
|
|
153
|
+
validation("proposal.decider-required", "a decision names who decides: the owner or an agent with a reason"),
|
|
154
|
+
validation("proposal.ids", "the proposal ids cannot be read"),
|
|
155
|
+
["proposal.own", "permission_error", "an agent never decides its own proposal"],
|
|
156
|
+
["proposal.owner-only", "permission_error", "only the owner decides this proposal"],
|
|
157
|
+
["proposals.agent-decisions-off", "permission_error", "agent decisions are turned off; the owner decides"],
|
|
158
|
+
["proposal.unknown-kind", "not_found", "no registered proposal kind holds this id"],
|
|
159
|
+
validation("relationship.exists", "a confirmed relationship already links them"),
|
|
135
160
|
validation("ranking.invalid", "the ranking is not valid"),
|
|
161
|
+
validation("ref.malformed", "the reference has a known kind but not its shape"),
|
|
162
|
+
validation("ref.unknown-kind", "the text names no kind of reference"),
|
|
163
|
+
validation("ref.wrong-kind", "the reference names another kind of thing than the argument takes"),
|
|
164
|
+
validation("ref.wrong-provider", "the reference belongs to another messenger; open it with that CLI"),
|
|
165
|
+
validation("ref.wrong-account", "the reference belongs to another account than this profile's"),
|
|
136
166
|
validation("selection.changed", "the stored selection changed meanwhile"),
|
|
137
167
|
validation("selection.invalid", "the selection is not valid"),
|
|
138
168
|
validation("search.kind", "the saved search belongs to another kind of search: messages or meetings"),
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { FOLDER_PROVIDERS, isFolderProvider, isKind, KINDS, type Kind, kindIn, k
|
|
|
12
12
|
export type { RankingGraphEvidence, RankingGraphLink } from "./domain/rankings-graph.js";
|
|
13
13
|
export type { RankingComponent, RankingInput, RankingMeasure, RankingOptions, RankingTarget, RankingWeights, ScorePreset, } from "./domain/rankings-options.js";
|
|
14
14
|
export { rankingOptions } from "./domain/rankings-options.js";
|
|
15
|
-
export { canonicalReference, formatReference, parseReference, type Reference } from "./domain/references.js";
|
|
15
|
+
export { canonicalReference, formatReference, looksLikeReference, parseReference, REF_KINDS, type Reference, type RefKindName, type RefKindSpec, } from "./domain/references.js";
|
|
16
16
|
export { normalizeTag, TAG_TYPES, type TagType } from "./domain/tags.js";
|
|
17
17
|
export { type AppErrors, DETAILS as CORE_DETAILS, failureContext, REASONS as CORE_REASONS } from "./errors/reasons.js";
|
|
18
18
|
export { canonicalMeetingReference, formatMeetingReference, type MeetingReference, parseMeetingReference, } from "./meetings/index.js";
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { parseMarkdownSpans } from "./domain/markdown-spans.js";
|
|
|
7
7
|
export { countedPoll } from "./domain/polls.js";
|
|
8
8
|
export { FOLDER_PROVIDERS, isFolderProvider, isKind, KINDS, kindIn, kindOf, PROVIDERS, providerNamed, providers, UNKNOWN_PROVIDER, useProviders, } from "./domain/providers.js";
|
|
9
9
|
export { rankingOptions } from "./domain/rankings-options.js";
|
|
10
|
-
export { canonicalReference, formatReference, parseReference } from "./domain/references.js";
|
|
10
|
+
export { canonicalReference, formatReference, looksLikeReference, parseReference, REF_KINDS, } from "./domain/references.js";
|
|
11
11
|
export { normalizeTag, TAG_TYPES } from "./domain/tags.js";
|
|
12
12
|
export { DETAILS as CORE_DETAILS, failureContext, REASONS as CORE_REASONS } from "./errors/reasons.js";
|
|
13
13
|
export { canonicalMeetingReference, formatMeetingReference, parseMeetingReference, } from "./meetings/index.js";
|
package/dist/mail/cli/index.js
CHANGED
|
@@ -74,11 +74,11 @@ const serverIssues = (server) => {
|
|
|
74
74
|
...FAILED[reason],
|
|
75
75
|
source: "server",
|
|
76
76
|
}));
|
|
77
|
-
const
|
|
78
|
-
? [{ code: "outcome_unknown", reason: "search.server-
|
|
77
|
+
const partial = server.partial
|
|
78
|
+
? [{ code: "outcome_unknown", reason: "search.server-partial", source: "server" }]
|
|
79
79
|
: [];
|
|
80
|
-
return failed.length ||
|
|
81
|
-
? [...failed, ...
|
|
80
|
+
return failed.length || partial.length
|
|
81
|
+
? [...failed, ...partial]
|
|
82
82
|
: [{ code: "outcome_unknown", reason: "search.server-capped", source: "server" }];
|
|
83
83
|
};
|
|
84
84
|
const searchCommand = (mail) => {
|
package/dist/mail/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { type AuthVerdicts, HEADER_FIELDS, headersOf, type MailHeaders, mailHead
|
|
|
7
7
|
export { type MailIdsInput, mailIds } from "./ids.js";
|
|
8
8
|
export { decodedLabelName, decodeMailboxName, labelName, systemLabel } from "./labels.js";
|
|
9
9
|
export { type Address, htmlToText, looksLikeHtml, MAX_ATTACHMENTS, MAX_BODY_CHARS, type ParsedMail, parseMail, } from "./parse.js";
|
|
10
|
+
export { PARTIAL, type PartialOutcome } from "./partial.js";
|
|
10
11
|
export { PRESET_NAMES, PRESETS, type PresetName, presetMatches } from "./presets.js";
|
|
11
12
|
export { domainsToAge, groupKey, type Probe, type ProbeGroup, probeMail, SUGGEST_RULE } from "./probe.js";
|
|
12
13
|
export { type MailFolderProgress, mailProgress } from "./progress.js";
|
|
@@ -21,5 +22,6 @@ export { DEMOTE_AT, type DomainAges, demotedSenders, type LookupResult, SCORE_VE
|
|
|
21
22
|
export { type MailCriteria, mailCriteriaOf, narrows } from "./server-query.js";
|
|
22
23
|
export { type MailServerAccess, type MailServerRequest, searchMailServer } from "./server-search.js";
|
|
23
24
|
export type { Listed, MailSource, Quick, Searched } from "./source.js";
|
|
25
|
+
export { SEARCH_REFUSED, searchRefused } from "./source.js";
|
|
24
26
|
export { listedOf, type StoredMail } from "./stored.js";
|
|
25
27
|
export { MAX_ATTACHMENT_CHARS, type MailAccount, type MailIndexed, type MailSyncOptions, type MailSyncResult, RETRYABLE_ATTACHMENTS, syncMail, } from "./sync.js";
|
package/dist/mail/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { HEADER_FIELDS, headersOf, mailHeadersOf } from "./headers.js";
|
|
|
7
7
|
export { mailIds } from "./ids.js";
|
|
8
8
|
export { decodedLabelName, decodeMailboxName, labelName, systemLabel } from "./labels.js";
|
|
9
9
|
export { htmlToText, looksLikeHtml, MAX_ATTACHMENTS, MAX_BODY_CHARS, parseMail, } from "./parse.js";
|
|
10
|
+
export { PARTIAL } from "./partial.js";
|
|
10
11
|
export { PRESET_NAMES, PRESETS, presetMatches } from "./presets.js";
|
|
11
12
|
export { domainsToAge, groupKey, probeMail, SUGGEST_RULE } from "./probe.js";
|
|
12
13
|
export { mailProgress } from "./progress.js";
|
|
@@ -20,5 +21,6 @@ export { FRAUD_SIGNALS, FREE_MAIL, registrableOf, SCORE_RULE, scoreGroup, WEIGHT
|
|
|
20
21
|
export { DEMOTE_AT, demotedSenders, SCORE_VERSION, scoreSenders, } from "./sender-scores.js";
|
|
21
22
|
export { mailCriteriaOf, narrows } from "./server-query.js";
|
|
22
23
|
export { searchMailServer } from "./server-search.js";
|
|
24
|
+
export { SEARCH_REFUSED, searchRefused } from "./source.js";
|
|
23
25
|
export { listedOf } from "./stored.js";
|
|
24
26
|
export { MAX_ATTACHMENT_CHARS, RETRYABLE_ATTACHMENTS, syncMail, } from "./sync.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BudgetStop } from "../background/budget.js";
|
|
2
|
+
import type { LoadEngine } from "../readers/types.js";
|
|
3
|
+
import type { MessageStore } from "../store/index.js";
|
|
4
|
+
import type { MailSource } from "./source.js";
|
|
5
|
+
/** The `emails.metadata` key of an email stored from its first bytes: `{ bytes }` read of its `size`. */
|
|
6
|
+
export declare const PARTIAL = "partial";
|
|
7
|
+
/** How many partial emails one sync reads whole at most; the rest wait for the next run. */
|
|
8
|
+
export declare const PARTIAL_PER_RUN = 100;
|
|
9
|
+
export interface PartialOutcome {
|
|
10
|
+
/** Read whole this run, the mark cleared. */
|
|
11
|
+
completed: number;
|
|
12
|
+
/** Still partial after this run. */
|
|
13
|
+
left: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reads whole the emails a server search stored from their first bytes, so the archive holds their attachments too.
|
|
17
|
+
* Sync never reads stored mail again, so without this step a partial email would stay partial. Gmail's are found by
|
|
18
|
+
* their id; elsewhere at a stored location, trusted only when the bytes read there carry the stored id.
|
|
19
|
+
*/
|
|
20
|
+
export declare const completePartial: ({ store, source, accountId, address, folders, maxBodyChars, maxAttachmentChars, loadEngine, now, take, signal, }: {
|
|
21
|
+
store: MessageStore;
|
|
22
|
+
source: MailSource;
|
|
23
|
+
accountId: number;
|
|
24
|
+
address: string;
|
|
25
|
+
/** Where Gmail's are looked for by id; asked only when a partial email is there to read. */
|
|
26
|
+
folders: () => Promise<readonly string[]>;
|
|
27
|
+
maxBodyChars: number;
|
|
28
|
+
maxAttachmentChars: number;
|
|
29
|
+
loadEngine: LoadEngine;
|
|
30
|
+
now: number;
|
|
31
|
+
/** A budgeted run's next request, or why there is none. */
|
|
32
|
+
take?: () => BudgetStop | null;
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
}) => Promise<PartialOutcome>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { FETCH_CHUNK, readAttachments } from "./attachments.js";
|
|
2
|
+
import { parsedEmail } from "./email-input.js";
|
|
3
|
+
import { mailHeadersOf } from "./headers.js";
|
|
4
|
+
import { parseMail } from "./parse.js";
|
|
5
|
+
/** The `emails.metadata` key of an email stored from its first bytes: `{ bytes }` read of its `size`. */
|
|
6
|
+
export const PARTIAL = "partial";
|
|
7
|
+
/** How many partial emails one sync reads whole at most; the rest wait for the next run. */
|
|
8
|
+
export const PARTIAL_PER_RUN = 100;
|
|
9
|
+
/**
|
|
10
|
+
* Reads whole the emails a server search stored from their first bytes, so the archive holds their attachments too.
|
|
11
|
+
* Sync never reads stored mail again, so without this step a partial email would stay partial. Gmail's are found by
|
|
12
|
+
* their id; elsewhere at a stored location, trusted only when the bytes read there carry the stored id.
|
|
13
|
+
*/
|
|
14
|
+
export const completePartial = async ({ store, source, accountId, address, folders, maxBodyChars, maxAttachmentChars, loadEngine, now, take, signal, }) => {
|
|
15
|
+
const { emails, total } = await store.mail.partialEmails(accountId, PARTIAL_PER_RUN);
|
|
16
|
+
if (emails.length === 0)
|
|
17
|
+
return { completed: 0, left: 0 };
|
|
18
|
+
const byId = new Map(emails.map((email) => [email.externalId, email]));
|
|
19
|
+
const items = new Map();
|
|
20
|
+
if (source.find) {
|
|
21
|
+
for (const folder of await folders()) {
|
|
22
|
+
const left = emails.filter((email) => !items.has(email)).map(({ externalId }) => externalId);
|
|
23
|
+
if (left.length === 0)
|
|
24
|
+
break;
|
|
25
|
+
if (take?.())
|
|
26
|
+
return { completed: 0, left: total };
|
|
27
|
+
for (const found of await source.find(left, folder)) {
|
|
28
|
+
const email = byId.get(found.messageId);
|
|
29
|
+
if (email && !items.has(email))
|
|
30
|
+
items.set(email, found);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else if (source.identify)
|
|
35
|
+
for (const email of emails) {
|
|
36
|
+
const place = email.locations[0];
|
|
37
|
+
if (place)
|
|
38
|
+
items.set(email, {
|
|
39
|
+
uid: String(place.uid),
|
|
40
|
+
folder: place.folder,
|
|
41
|
+
receivedAt: new Date(email.receivedAt ?? 0).toISOString(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
let completed = 0;
|
|
45
|
+
const wanted = [...items];
|
|
46
|
+
for (let start = 0; start < wanted.length; start += FETCH_CHUNK) {
|
|
47
|
+
if (signal?.aborted || take?.())
|
|
48
|
+
break;
|
|
49
|
+
const chunk = wanted.slice(start, start + FETCH_CHUNK);
|
|
50
|
+
let raws;
|
|
51
|
+
try {
|
|
52
|
+
raws = await source.fetchRaw(chunk.map(([, item]) => item));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// A partial email stays readable as it is; the next run tries again.
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
const attachments = [];
|
|
59
|
+
for (const [index, [email, item]] of chunk.entries()) {
|
|
60
|
+
const raw = raws[index];
|
|
61
|
+
if (!(raw instanceof Uint8Array))
|
|
62
|
+
continue;
|
|
63
|
+
// A UID means another message after a UIDVALIDITY change; only the stored id proves it is this one.
|
|
64
|
+
if (!source.find && source.identify?.(raw, item).messageId !== email.externalId)
|
|
65
|
+
continue;
|
|
66
|
+
try {
|
|
67
|
+
const mail = await parseMail(raw, maxBodyChars);
|
|
68
|
+
const files = await readAttachments(mail, email.externalId, { loadEngine, maxAttachmentChars, at: now, ...(signal ? { signal } : {}) }, attachments);
|
|
69
|
+
const { [PARTIAL]: _partial, ...metadata } = email.metadata ?? {};
|
|
70
|
+
const whole = { ...item, messageId: email.externalId, threadId: email.threadExternalId };
|
|
71
|
+
await store.mail.saveThread({
|
|
72
|
+
accountId,
|
|
73
|
+
externalId: email.threadExternalId,
|
|
74
|
+
now,
|
|
75
|
+
emails: [
|
|
76
|
+
{
|
|
77
|
+
...parsedEmail(mail, whole, mailHeadersOf(raw), address),
|
|
78
|
+
receivedAt: email.receivedAt,
|
|
79
|
+
size: raw.byteLength,
|
|
80
|
+
metadata,
|
|
81
|
+
attachments: files,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
completed++;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
if (signal?.aborted)
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { completed, left: total - completed };
|
|
94
|
+
};
|
|
@@ -9,6 +9,7 @@ import { parsedEmail } from "./email-input.js";
|
|
|
9
9
|
import { factsOf } from "./facts.js";
|
|
10
10
|
import { mailHeadersOf } from "./headers.js";
|
|
11
11
|
import { parseMail } from "./parse.js";
|
|
12
|
+
import { PARTIAL } from "./partial.js";
|
|
12
13
|
import { storedMailRules } from "./rule-stored.js";
|
|
13
14
|
import { ruleTagger, settleRuleTags } from "./rule-tags.js";
|
|
14
15
|
import { decide, tagsOf } from "./rules.js";
|
|
@@ -84,6 +85,7 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
84
85
|
const ruled = new Map();
|
|
85
86
|
const inputs = [];
|
|
86
87
|
const saved = [];
|
|
88
|
+
let cutShort = 0;
|
|
87
89
|
let opening;
|
|
88
90
|
let openFailure;
|
|
89
91
|
let unsupported = false;
|
|
@@ -146,17 +148,12 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
146
148
|
sources.set(sourceKey(item.threadId, item.messageId), held ? "both" : "server");
|
|
147
149
|
if (held)
|
|
148
150
|
continue;
|
|
149
|
-
// A big message would hold the connection and the parser past the bound; sync reads it whole later.
|
|
150
|
-
if ((item.size ?? 0) > SERVER_BOUNDS.maxMessageBytes) {
|
|
151
|
-
report.tooLarge = (report.tooLarge ?? 0) + 1;
|
|
152
|
-
report.complete = false;
|
|
153
|
-
sources.delete(sourceKey(item.threadId, item.messageId));
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
151
|
fresh.push(item);
|
|
157
152
|
ruled.set(item, { ruleId: rule?.id ?? null, tags: tagsOf(rules, facts(item)) });
|
|
158
153
|
}
|
|
159
|
-
|
|
154
|
+
// A big message would hold the connection and the parser past the bound: its first bytes carry the text.
|
|
155
|
+
const bytes = SERVER_BOUNDS.textBytes;
|
|
156
|
+
const raws = fresh.length && open ? await source.fetchRaw(fresh, { bytes }) : [];
|
|
160
157
|
// Parsing and reading attachments stay inside the bound: after it, nothing more is read or kept.
|
|
161
158
|
const now = Date.now();
|
|
162
159
|
for (const [index, item] of fresh.entries()) {
|
|
@@ -166,8 +163,12 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
166
163
|
try {
|
|
167
164
|
if (raw === undefined || raw instanceof Error)
|
|
168
165
|
throw raw ?? new Error("not read");
|
|
166
|
+
const cut = raw.byteLength >= bytes && (item.size === undefined || item.size > raw.byteLength);
|
|
169
167
|
const mail = await parseMail(raw);
|
|
170
|
-
|
|
168
|
+
// An attachment cut short is not the file: none is kept until the sync reads the whole message.
|
|
169
|
+
const files = cut
|
|
170
|
+
? []
|
|
171
|
+
: await readAttachments(mail, item.messageId, { loadEngine: importEngine, maxAttachmentChars: MAX_ATTACHMENT_CHARS, at: now, signal: stop }, []);
|
|
171
172
|
if (!open)
|
|
172
173
|
return;
|
|
173
174
|
const folder = item.folder;
|
|
@@ -189,12 +190,15 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
189
190
|
: { locations: [{ folder, uidvalidity: validity, uid: Number(item.uid) }] }),
|
|
190
191
|
...(item.seen === undefined ? {} : { read: item.seen }),
|
|
191
192
|
...(item.flagged === undefined ? {} : { flagged: item.flagged }),
|
|
193
|
+
...(cut ? { metadata: { [PARTIAL]: { bytes: raw.byteLength } } } : {}),
|
|
192
194
|
mailRuleId: ruled.get(item)?.ruleId ?? null,
|
|
193
195
|
attachments: files,
|
|
194
196
|
},
|
|
195
197
|
],
|
|
196
198
|
});
|
|
197
199
|
saved.push(item);
|
|
200
|
+
if (cut)
|
|
201
|
+
cutShort++;
|
|
198
202
|
}
|
|
199
203
|
catch {
|
|
200
204
|
sources.delete(sourceKey(item.threadId, item.messageId));
|
|
@@ -248,6 +252,10 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
248
252
|
}
|
|
249
253
|
await settleRuleTags(store, accountId, rules, tagger.outcome());
|
|
250
254
|
report.new = inputs.length;
|
|
255
|
+
if (cutShort) {
|
|
256
|
+
report.partial = cutShort;
|
|
257
|
+
report.complete = false;
|
|
258
|
+
}
|
|
251
259
|
report.returned = sources.size;
|
|
252
260
|
return { report, sources };
|
|
253
261
|
};
|
package/dist/mail/source.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { MailHeaders } from "./headers.js";
|
|
2
2
|
import type { FileStamp } from "./resume.js";
|
|
3
3
|
import type { MailCriteria } from "./server-query.js";
|
|
4
|
+
/**
|
|
5
|
+
* The reason of the CliError `search`, `query` and `present` throw when the server answers SEARCH with NO or BAD:
|
|
6
|
+
* read as "nothing found", it would look like an empty folder and prove its stored mail deleted.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SEARCH_REFUSED = "mail.search-refused";
|
|
9
|
+
export declare const searchRefused: (error: unknown) => boolean;
|
|
4
10
|
export interface Listed {
|
|
5
11
|
uid: string;
|
|
6
12
|
/** Gmail's own ids: the same in every folder, and they survive a UID reset. */
|
|
@@ -94,6 +100,11 @@ export interface MailSource {
|
|
|
94
100
|
present?(uids: readonly string[], folder: string): Promise<string[]>;
|
|
95
101
|
/** Puts labels back on messages in `folder`, by Gmail id. */
|
|
96
102
|
label?(messageIds: readonly string[], folder: string, labels: readonly string[]): Promise<void>;
|
|
97
|
-
/**
|
|
98
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Each item's raw RFC 5322 bytes, in order; one that could not be read is an Error, not a failed call. With
|
|
105
|
+
* `bytes`, at most the first `bytes` of each: a message cut short comes back shorter than its `size`.
|
|
106
|
+
*/
|
|
107
|
+
fetchRaw(items: Quick[], options?: {
|
|
108
|
+
bytes?: number;
|
|
109
|
+
}): Promise<(Uint8Array | Error)[]>;
|
|
99
110
|
}
|
package/dist/mail/source.js
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
/**
|
|
3
|
+
* The reason of the CliError `search`, `query` and `present` throw when the server answers SEARCH with NO or BAD:
|
|
4
|
+
* read as "nothing found", it would look like an empty folder and prove its stored mail deleted.
|
|
5
|
+
*/
|
|
6
|
+
export const SEARCH_REFUSED = "mail.search-refused";
|
|
7
|
+
export const searchRefused = (error) => error instanceof CliError && error.details.reason === SEARCH_REFUSED;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { BudgetStop, RunBudget } from "../background/budget.js";
|
|
2
|
+
import type { LoadEngine } from "../readers/types.js";
|
|
3
|
+
import type { AccountKey, MessageStore } from "../store/index.js";
|
|
4
|
+
import type { PartialOutcome } from "./partial.js";
|
|
5
|
+
import type { TagOutcome } from "./rule-tags.js";
|
|
6
|
+
import type { MailRule } from "./rules.js";
|
|
7
|
+
import type { DomainAge } from "./score.js";
|
|
8
|
+
export interface MailAccount {
|
|
9
|
+
/** The mailbox address: the store's account id. */
|
|
10
|
+
address: string;
|
|
11
|
+
mode?: "gmail" | "imap" | "mbox";
|
|
12
|
+
folders?: string[];
|
|
13
|
+
rules?: MailRule[];
|
|
14
|
+
}
|
|
15
|
+
/** What an index step reports, as memo's embedding does; the sync only passes it on. */
|
|
16
|
+
export interface MailIndexed {
|
|
17
|
+
chats: number;
|
|
18
|
+
chunks: number;
|
|
19
|
+
left: boolean;
|
|
20
|
+
notEmbedded?: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface MailSyncResult {
|
|
23
|
+
account: string;
|
|
24
|
+
since: string;
|
|
25
|
+
listed: number;
|
|
26
|
+
saved: number;
|
|
27
|
+
alreadyStored: number;
|
|
28
|
+
/** New emails a skip rule kept out: never read, never stored. */
|
|
29
|
+
skipped: number;
|
|
30
|
+
/** New emails an envelope rule recorded: sender, date, size and labels, never their text. */
|
|
31
|
+
envelopes: number;
|
|
32
|
+
deleted: number;
|
|
33
|
+
/** False when `max` stopped the run before every new message was read; the next run continues. */
|
|
34
|
+
complete: boolean;
|
|
35
|
+
/** `partial`: a budgeted run stopped before the whole window was read; the next run goes on. */
|
|
36
|
+
state?: "partial" | "complete";
|
|
37
|
+
/** Why a budgeted run stopped early: its request budget, its deadline, or a cancel. */
|
|
38
|
+
stopped?: BudgetStop;
|
|
39
|
+
/** Messages of the window this run did not get to read: left by a budget, or by `max`. */
|
|
40
|
+
remaining?: number;
|
|
41
|
+
/** Requests a budgeted run spent. */
|
|
42
|
+
requests?: number;
|
|
43
|
+
/** Emails that could not be read or parsed; they are tried again next run. */
|
|
44
|
+
unreadable?: {
|
|
45
|
+
message: string;
|
|
46
|
+
reason: string;
|
|
47
|
+
}[];
|
|
48
|
+
/** Why stored messages missing at the source were left alone, when they were. */
|
|
49
|
+
deletionsSkipped?: string;
|
|
50
|
+
/** Folders whose server refused SEARCH: left unread, so the run is partial and proves no deletion. */
|
|
51
|
+
refusedFolders?: string[];
|
|
52
|
+
/** "resumed": only mail not done before was listed; "full": every message in the window was. */
|
|
53
|
+
pass?: "full" | "resumed";
|
|
54
|
+
indexed?: MailIndexed;
|
|
55
|
+
coverage?: {
|
|
56
|
+
folders: string[];
|
|
57
|
+
since: string;
|
|
58
|
+
until: string;
|
|
59
|
+
state: "window" | "partial";
|
|
60
|
+
identity: string;
|
|
61
|
+
};
|
|
62
|
+
attachments?: {
|
|
63
|
+
message: string;
|
|
64
|
+
part: number;
|
|
65
|
+
status: string;
|
|
66
|
+
}[];
|
|
67
|
+
/** Rule tags put on mail as an existing label the store pointed to. */
|
|
68
|
+
tagsReused?: TagOutcome["reused"];
|
|
69
|
+
/** Rule tags the store refused with nothing to reuse; that mail is stored untagged. */
|
|
70
|
+
tagsSkipped?: TagOutcome["skipped"];
|
|
71
|
+
/** Emails a server search stored from their first bytes, read whole this run, and those still partial. */
|
|
72
|
+
partial?: PartialOutcome;
|
|
73
|
+
/** `--retry-attachments`: emails read again for attachments a missing reader left unread. */
|
|
74
|
+
retried?: {
|
|
75
|
+
emails: number;
|
|
76
|
+
extracted: number;
|
|
77
|
+
stillFailed: number;
|
|
78
|
+
missing: number;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export interface MailSyncOptions {
|
|
82
|
+
account: MailAccount;
|
|
83
|
+
since: Date;
|
|
84
|
+
max?: number;
|
|
85
|
+
maxBodyChars?: number;
|
|
86
|
+
maxAttachmentChars?: number;
|
|
87
|
+
now?: () => number;
|
|
88
|
+
/** Domain registration dates already known, for the sender score a rule asks for. */
|
|
89
|
+
ages?: ReadonlyMap<string, DomainAge>;
|
|
90
|
+
loadEngine?: LoadEngine;
|
|
91
|
+
retryAttachments?: boolean;
|
|
92
|
+
/** List every message in the window, not only those an earlier run left: proves deletions, refreshes labels. */
|
|
93
|
+
rescan?: boolean;
|
|
94
|
+
/** `false` for a spot read, like the copy of a sent message: the run leaves the saved coverage as the last full run set it. */
|
|
95
|
+
saveCoverage?: boolean;
|
|
96
|
+
/** A window smaller than this is listed whole every run: it costs seconds and keeps flags and deletions current. */
|
|
97
|
+
resumeFrom?: number;
|
|
98
|
+
/** Runs after the mail is saved, so chats and mail still waiting for vectors are indexed; its answer is `indexed`. */
|
|
99
|
+
index?: (store: MessageStore, key: AccountKey) => Promise<MailIndexed>;
|
|
100
|
+
/** Stops between batches of emails, after saving the resume point. */
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
/**
|
|
103
|
+
* Makes the run budgeted: it reads the window in rounds, one request of the budget per call to the source, saves
|
|
104
|
+
* after each, stops when the budget says so and answers `state: "partial"`. A message the source could not give
|
|
105
|
+
* is tried once more at the end of the run and, if it still fails, left for the next one. `max` defaults to no limit.
|
|
106
|
+
*/
|
|
107
|
+
budget?: RunBudget;
|
|
108
|
+
/** Pauses before a round is read again after the store was locked; tests pass one that does not wait. */
|
|
109
|
+
wait?: (ms: number) => Promise<void>;
|
|
110
|
+
/** `false` for a spot read: emails a server search stored from their first bytes are not read whole this run. */
|
|
111
|
+
completePartial?: boolean;
|
|
112
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/mail/sync.d.ts
CHANGED
|
@@ -1,109 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
import { type MailRule } from "./rules.js";
|
|
6
|
-
import type { DomainAge } from "./score.js";
|
|
7
|
-
import type { MailSource } from "./source.js";
|
|
8
|
-
export interface MailAccount {
|
|
9
|
-
/** The mailbox address: the store's account id. */
|
|
10
|
-
address: string;
|
|
11
|
-
mode?: "gmail" | "imap" | "mbox";
|
|
12
|
-
folders?: string[];
|
|
13
|
-
rules?: MailRule[];
|
|
14
|
-
}
|
|
15
|
-
/** What an index step reports, as memo's embedding does; the sync only passes it on. */
|
|
16
|
-
export interface MailIndexed {
|
|
17
|
-
chats: number;
|
|
18
|
-
chunks: number;
|
|
19
|
-
left: boolean;
|
|
20
|
-
notEmbedded?: unknown;
|
|
21
|
-
}
|
|
22
|
-
export interface MailSyncResult {
|
|
23
|
-
account: string;
|
|
24
|
-
since: string;
|
|
25
|
-
listed: number;
|
|
26
|
-
saved: number;
|
|
27
|
-
alreadyStored: number;
|
|
28
|
-
/** New emails a skip rule kept out: never read, never stored. */
|
|
29
|
-
skipped: number;
|
|
30
|
-
/** New emails an envelope rule recorded: sender, date, size and labels, never their text. */
|
|
31
|
-
envelopes: number;
|
|
32
|
-
deleted: number;
|
|
33
|
-
/** False when `max` stopped the run before every new message was read; the next run continues. */
|
|
34
|
-
complete: boolean;
|
|
35
|
-
/** `partial`: a budgeted run stopped before the whole window was read; the next run goes on. */
|
|
36
|
-
state?: "partial" | "complete";
|
|
37
|
-
/** Why a budgeted run stopped early: its request budget, its deadline, or a cancel. */
|
|
38
|
-
stopped?: BudgetStop;
|
|
39
|
-
/** Messages of the window this run did not get to read: left by a budget, or by `max`. */
|
|
40
|
-
remaining?: number;
|
|
41
|
-
/** Requests a budgeted run spent. */
|
|
42
|
-
requests?: number;
|
|
43
|
-
/** Emails that could not be read or parsed; they are tried again next run. */
|
|
44
|
-
unreadable?: {
|
|
45
|
-
message: string;
|
|
46
|
-
reason: string;
|
|
47
|
-
}[];
|
|
48
|
-
/** Why stored messages missing at the source were left alone, when they were. */
|
|
49
|
-
deletionsSkipped?: string;
|
|
50
|
-
/** "resumed": only mail not done before was listed; "full": every message in the window was. */
|
|
51
|
-
pass?: "full" | "resumed";
|
|
52
|
-
indexed?: MailIndexed;
|
|
53
|
-
coverage?: {
|
|
54
|
-
folders: string[];
|
|
55
|
-
since: string;
|
|
56
|
-
until: string;
|
|
57
|
-
state: "window" | "partial";
|
|
58
|
-
identity: string;
|
|
59
|
-
};
|
|
60
|
-
attachments?: {
|
|
61
|
-
message: string;
|
|
62
|
-
part: number;
|
|
63
|
-
status: string;
|
|
64
|
-
}[];
|
|
65
|
-
/** Rule tags put on mail as an existing label the store pointed to. */
|
|
66
|
-
tagsReused?: TagOutcome["reused"];
|
|
67
|
-
/** Rule tags the store refused with nothing to reuse; that mail is stored untagged. */
|
|
68
|
-
tagsSkipped?: TagOutcome["skipped"];
|
|
69
|
-
/** `--retry-attachments`: emails read again for attachments a missing reader left unread. */
|
|
70
|
-
retried?: {
|
|
71
|
-
emails: number;
|
|
72
|
-
extracted: number;
|
|
73
|
-
stillFailed: number;
|
|
74
|
-
missing: number;
|
|
75
|
-
};
|
|
76
|
-
}
|
|
1
|
+
import type { MessageStore } from "../store/index.js";
|
|
2
|
+
import { type MailSource } from "./source.js";
|
|
3
|
+
import type { MailSyncOptions, MailSyncResult } from "./sync-types.js";
|
|
4
|
+
export type { MailAccount, MailIndexed, MailSyncOptions, MailSyncResult } from "./sync-types.js";
|
|
77
5
|
export { MAX_ATTACHMENT_CHARS } from "./attachments.js";
|
|
78
6
|
export { RETRYABLE_ATTACHMENTS } from "./retry.js";
|
|
79
|
-
export interface MailSyncOptions {
|
|
80
|
-
account: MailAccount;
|
|
81
|
-
since: Date;
|
|
82
|
-
max?: number;
|
|
83
|
-
maxBodyChars?: number;
|
|
84
|
-
maxAttachmentChars?: number;
|
|
85
|
-
now?: () => number;
|
|
86
|
-
/** Domain registration dates already known, for the sender score a rule asks for. */
|
|
87
|
-
ages?: ReadonlyMap<string, DomainAge>;
|
|
88
|
-
loadEngine?: LoadEngine;
|
|
89
|
-
retryAttachments?: boolean;
|
|
90
|
-
/** List every message in the window, not only those an earlier run left: proves deletions, refreshes labels. */
|
|
91
|
-
rescan?: boolean;
|
|
92
|
-
/** `false` for a spot read, like the copy of a sent message: the run leaves the saved coverage as the last full run set it. */
|
|
93
|
-
saveCoverage?: boolean;
|
|
94
|
-
/** A window smaller than this is listed whole every run: it costs seconds and keeps flags and deletions current. */
|
|
95
|
-
resumeFrom?: number;
|
|
96
|
-
/** Runs after the mail is saved, so chats and mail still waiting for vectors are indexed; its answer is `indexed`. */
|
|
97
|
-
index?: (store: MessageStore, key: AccountKey) => Promise<MailIndexed>;
|
|
98
|
-
/** Stops between batches of emails, after saving the resume point. */
|
|
99
|
-
signal?: AbortSignal;
|
|
100
|
-
/**
|
|
101
|
-
* Makes the run budgeted: it reads the window in rounds, one request of the budget per call to the source, saves
|
|
102
|
-
* after each, stops when the budget says so and answers `state: "partial"`. A message the source could not give
|
|
103
|
-
* is tried once more at the end of the run and, if it still fails, left for the next one. `max` defaults to no limit.
|
|
104
|
-
*/
|
|
105
|
-
budget?: RunBudget;
|
|
106
|
-
/** Pauses before a round is read again after the store was locked; tests pass one that does not wait. */
|
|
107
|
-
wait?: (ms: number) => Promise<void>;
|
|
108
|
-
}
|
|
109
7
|
export declare const syncMail: (source: MailSource, store: MessageStore, options: MailSyncOptions) => Promise<MailSyncResult>;
|