@remit/data-ports 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@remit/data-ports",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.ts",
10
+ "default": "./src/index.ts"
11
+ },
12
+ "./conformance": {
13
+ "types": "./src/conformance/index.ts",
14
+ "default": "./src/conformance/index.ts"
15
+ },
16
+ "./errors": {
17
+ "types": "./src/errors.ts",
18
+ "default": "./src/errors.ts"
19
+ },
20
+ "./id": {
21
+ "types": "./src/id.ts",
22
+ "default": "./src/id.ts"
23
+ },
24
+ "./account-settings": {
25
+ "types": "./src/account-settings.ts",
26
+ "default": "./src/account-settings.ts"
27
+ },
28
+ "./wellknown": {
29
+ "types": "./src/wellknown.ts",
30
+ "default": "./src/wellknown.ts"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "test:typecheck": "tsgo --noEmit"
35
+ },
36
+ "dependencies": {
37
+ "@remit/domain-enums": "*",
38
+ "short-uuid": "*",
39
+ "uuid": "*",
40
+ "zod": "*"
41
+ },
42
+ "devDependencies": {
43
+ "@remit/api-zod-schemas": "*"
44
+ },
45
+ "license": "MIT",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/remit-mail/remit.git",
52
+ "directory": "packages/data-ports"
53
+ }
54
+ }
@@ -0,0 +1,192 @@
1
+ import { AccountSettingName } from "@remit/domain-enums";
2
+ import { z } from "zod";
3
+
4
+ export type AccountSettingNameValue =
5
+ (typeof AccountSettingName)[keyof typeof AccountSettingName];
6
+
7
+ /**
8
+ * Per-`kind` value schemas, one per variant of the `AccountSettingValue`
9
+ * discriminated union in TypeSpec (`AccountSetting.tsp`). Authored here because
10
+ * the registry is the binding authority for what a value must satisfy — these
11
+ * are the contract every read and write is validated against.
12
+ */
13
+ export const BooleanSettingSchema = z.object({
14
+ kind: z.literal("Boolean"),
15
+ value: z.boolean(),
16
+ });
17
+ export const StringSettingSchema = z.object({
18
+ kind: z.literal("String"),
19
+ value: z.string(),
20
+ });
21
+ export const NumberSettingSchema = z.object({
22
+ kind: z.literal("Number"),
23
+ value: z.number(),
24
+ });
25
+ export const StringListSettingSchema = z.object({
26
+ kind: z.literal("StringList"),
27
+ value: z.array(z.string()),
28
+ });
29
+ export const MapSettingSchema = z.object({
30
+ kind: z.literal("Map"),
31
+ value: z.record(z.string(), z.string()),
32
+ });
33
+ /**
34
+ * MutedFlag-valued setting. The value is a rich mute object (`MutedFlag` in
35
+ * TypeSpec / `@remit/api-openapi-types`): a boolean plus audit metadata. The
36
+ * schema is authored here — generated packages ship only `.d.ts` for this shape,
37
+ * so the registry owns the runtime contract a relocated mute flag is validated
38
+ * against (RFC 032 lesson from the signature slice).
39
+ */
40
+ export const MutedFlagSettingSchema = z.object({
41
+ kind: z.literal("MutedFlag"),
42
+ value: z.object({
43
+ value: z.boolean(),
44
+ setAt: z.number(),
45
+ setBy: z.string().optional(),
46
+ expiresAt: z.number().optional(),
47
+ reason: z.string().optional(),
48
+ }),
49
+ });
50
+
51
+ /**
52
+ * RFC 032 Tier 1 registry: binds each setting `name` to the zod schema its
53
+ * `value` must satisfy. The discriminated union (`kind`) types the shape on the
54
+ * wire; this registry binds a name to one concrete shape, so a read or write of
55
+ * a given setting is validated against exactly the variant it requires.
56
+ *
57
+ * Adding a setting key is a deliberate change: extend the `AccountSettingName`
58
+ * enum in TypeSpec and add an entry here — never an ad-hoc, unvalidated value.
59
+ */
60
+ export const accountSettingRegistry = {
61
+ [AccountSettingName.Theme]: StringSettingSchema,
62
+ [AccountSettingName.Density]: StringSettingSchema,
63
+ [AccountSettingName.DefaultComposerFormat]: StringSettingSchema,
64
+ [AccountSettingName.PinnedFolders]: StringListSettingSchema,
65
+ [AccountSettingName.AccountSignaturePlainText]: StringSettingSchema,
66
+ [AccountSettingName.AccountSignatureHtml]: StringSettingSchema,
67
+ [AccountSettingName.AccountDisplayName]: StringSettingSchema,
68
+ [AccountSettingName.AccountMuted]: MutedFlagSettingSchema,
69
+ [AccountSettingName.MailboxDisplayName]: StringSettingSchema,
70
+ [AccountSettingName.MailboxMuted]: MutedFlagSettingSchema,
71
+ [AccountSettingName.FolderRoleAppointment]: StringSettingSchema,
72
+ /**
73
+ * Deprecated tombstone: superseded by `FolderRoleAppointment` (RFC 032
74
+ * exclusive-folder-appointment, #976). Kept only so `baseSettingName`
75
+ * recognises leftover `MailboxRole#<mailboxId>` rows written by the #963/#964
76
+ * backfill instead of throwing `Unknown account setting name`; the read-side
77
+ * groupers (`groupAccountOverrides`, `groupMailboxOverrides`,
78
+ * `groupFolderAppointmentsByAccount`) already skip any base they don't
79
+ * explicitly handle, so a `MailboxRole` row is ignored, not surfaced. Do not
80
+ * write new rows under this name. Remove once the row-deletion migration ships.
81
+ */
82
+ [AccountSettingName.MailboxRole]: StringSettingSchema,
83
+ } as const satisfies Record<AccountSettingNameValue, z.ZodTypeAny>;
84
+
85
+ export type AccountSettingRegistry = typeof accountSettingRegistry;
86
+
87
+ export type AccountSettingValueFor<N extends AccountSettingNameValue> = z.infer<
88
+ AccountSettingRegistry[N]
89
+ >;
90
+
91
+ /**
92
+ * Separator between a setting's base enum member and its per-target suffix in a
93
+ * composite stored name (e.g. `AccountSignaturePlainText#<accountId>`). Some
94
+ * settings are per-account, but `AccountSetting` is keyed per accountConfigId, so
95
+ * the stored `name` encodes the target after this separator. The registry still
96
+ * validates by the base member; readers filter by the suffix.
97
+ */
98
+ export const SETTING_NAME_SEPARATOR = "#";
99
+
100
+ /**
101
+ * A persisted setting `name`. Either a plain closed-enum member or a composite
102
+ * `<base>#<suffix>` for a per-target setting. Always a `string` whose first
103
+ * segment is a known base member.
104
+ */
105
+ export type StoredSettingName = string;
106
+
107
+ /** Compose the stored name for a per-target setting (e.g. one signature per account). */
108
+ export function composeSettingName(
109
+ base: AccountSettingNameValue,
110
+ target: string,
111
+ ): StoredSettingName {
112
+ return `${base}${SETTING_NAME_SEPARATOR}${target}`;
113
+ }
114
+
115
+ const isAccountSettingNameValue = (
116
+ value: string,
117
+ ): value is AccountSettingNameValue =>
118
+ Object.hasOwn(accountSettingRegistry, value);
119
+
120
+ /**
121
+ * Derive the base enum member from a stored name. Strips the `#<suffix>` from a
122
+ * composite name; returns a plain name unchanged. Throws when the base segment
123
+ * is not a registered setting (let-it-crash — an unknown key is a programmer
124
+ * error, never an ad-hoc column).
125
+ */
126
+ export function baseSettingName(
127
+ name: StoredSettingName,
128
+ ): AccountSettingNameValue {
129
+ const base = name.split(SETTING_NAME_SEPARATOR)[0] ?? "";
130
+ if (!isAccountSettingNameValue(base)) {
131
+ throw new Error(`Unknown account setting name: ${name}`);
132
+ }
133
+ return base;
134
+ }
135
+
136
+ /**
137
+ * Every value shape the registry can hold. Useful where a value is handled
138
+ * generically (e.g. the persisted `value` attribute) rather than per-name.
139
+ */
140
+ export type AnyAccountSettingValue = z.infer<
141
+ | typeof BooleanSettingSchema
142
+ | typeof StringSettingSchema
143
+ | typeof NumberSettingSchema
144
+ | typeof StringListSettingSchema
145
+ | typeof MapSettingSchema
146
+ | typeof MutedFlagSettingSchema
147
+ >;
148
+
149
+ /**
150
+ * Parse-and-narrow a value against the schema registered for `name`. Accepts a
151
+ * plain enum member or a composite `<base>#<suffix>` name; validation resolves
152
+ * to the base member's schema. Throws a ZodError on mismatch (let-it-crash);
153
+ * returns the value typed to the variant the name requires.
154
+ */
155
+ export function parseAccountSettingValue<N extends AccountSettingNameValue>(
156
+ name: N,
157
+ value: unknown,
158
+ ): AccountSettingValueFor<N>;
159
+ export function parseAccountSettingValue(
160
+ name: StoredSettingName,
161
+ value: unknown,
162
+ ): AnyAccountSettingValue;
163
+ export function parseAccountSettingValue(
164
+ name: StoredSettingName,
165
+ value: unknown,
166
+ ): AnyAccountSettingValue {
167
+ const schema = accountSettingRegistry[baseSettingName(name)];
168
+ return schema.parse(value) as AnyAccountSettingValue;
169
+ }
170
+
171
+ /**
172
+ * Non-throwing variant: returns the zod SafeParse result so a caller with a
173
+ * recovery path can branch on `success`. Accepts composite names like above.
174
+ */
175
+ export function safeParseAccountSettingValue<N extends AccountSettingNameValue>(
176
+ name: N,
177
+ value: unknown,
178
+ ): z.SafeParseReturnType<unknown, AccountSettingValueFor<N>>;
179
+ export function safeParseAccountSettingValue(
180
+ name: StoredSettingName,
181
+ value: unknown,
182
+ ): z.SafeParseReturnType<unknown, AnyAccountSettingValue>;
183
+ export function safeParseAccountSettingValue(
184
+ name: StoredSettingName,
185
+ value: unknown,
186
+ ): z.SafeParseReturnType<unknown, AnyAccountSettingValue> {
187
+ const schema = accountSettingRegistry[baseSettingName(name)];
188
+ return schema.safeParse(value) as z.SafeParseReturnType<
189
+ unknown,
190
+ AnyAccountSettingValue
191
+ >;
192
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * A backend's binding for a conformance suite: how to build the repository
3
+ * under test, how to tear its backing store down, and the two behaviours the
4
+ * suite cannot express portably — id minting and not-found detection, both of
5
+ * which differ per implementation.
6
+ */
7
+ export interface RepositoryConformanceHarness<TRepo> {
8
+ createRepository(): Promise<TRepo>;
9
+ teardown(): Promise<void>;
10
+ makeId(): string;
11
+ isNotFoundError(error: unknown): boolean;
12
+ }
@@ -0,0 +1,2 @@
1
+ export type { RepositoryConformanceHarness } from "./harness.js";
2
+ export { labelRepositoryConformance } from "./label.js";
@@ -0,0 +1,95 @@
1
+ import assert from "node:assert/strict";
2
+ import { after, before, describe, test } from "node:test";
3
+ import { LabelColor } from "@remit/domain-enums";
4
+ import type { ILabelRepository } from "../interfaces/label.js";
5
+ import type { RepositoryConformanceHarness } from "./harness.js";
6
+
7
+ export function labelRepositoryConformance(
8
+ harness: RepositoryConformanceHarness<ILabelRepository>,
9
+ ): void {
10
+ describe("ILabelRepository conformance", () => {
11
+ let repo: ILabelRepository;
12
+
13
+ before(async () => {
14
+ repo = await harness.createRepository();
15
+ });
16
+
17
+ after(() => harness.teardown());
18
+
19
+ test("create derives normalizedName and defaults color", async () => {
20
+ const accountConfigId = harness.makeId();
21
+
22
+ const label = await repo.create({
23
+ accountConfigId,
24
+ name: " Receipts ",
25
+ });
26
+
27
+ assert.equal(label.name, " Receipts ");
28
+ assert.equal(label.normalizedName, "receipts");
29
+ assert.equal(label.color, LabelColor.Default);
30
+ });
31
+
32
+ test("get throws a not-found error for a missing label", async () => {
33
+ await assert.rejects(
34
+ repo.get(harness.makeId(), harness.makeId()),
35
+ (error) => harness.isNotFoundError(error),
36
+ );
37
+ });
38
+
39
+ test("update re-derives normalizedName when the name changes", async () => {
40
+ const accountConfigId = harness.makeId();
41
+ const label = await repo.create({ accountConfigId, name: "Old Name" });
42
+
43
+ const updated = await repo.update(accountConfigId, label.labelId, {
44
+ name: "New Name",
45
+ });
46
+
47
+ assert.equal(updated.name, "New Name");
48
+ assert.equal(updated.normalizedName, "new name");
49
+ });
50
+
51
+ test("update throws a not-found error for a missing label", async () => {
52
+ await assert.rejects(
53
+ repo.update(harness.makeId(), harness.makeId(), { name: "x" }),
54
+ (error) => harness.isNotFoundError(error),
55
+ );
56
+ });
57
+
58
+ test("listByAccountConfig scopes to the account", async () => {
59
+ const accountConfigId = harness.makeId();
60
+ const other = harness.makeId();
61
+
62
+ const mine = await repo.create({ accountConfigId, name: "Mine" });
63
+ await repo.create({ accountConfigId: other, name: "Foreign" });
64
+
65
+ const labels = await repo.listByAccountConfig(accountConfigId);
66
+ assert.equal(labels.length, 1);
67
+ assert.equal(labels[0]?.labelId, mine.labelId);
68
+ });
69
+
70
+ test("findByNormalizedName dedupes case- and whitespace-insensitively", async () => {
71
+ const accountConfigId = harness.makeId();
72
+ const label = await repo.create({ accountConfigId, name: "Work" });
73
+
74
+ const found = await repo.findByNormalizedName(accountConfigId, "WORK");
75
+ assert.equal(found?.labelId, label.labelId);
76
+
77
+ const missing = await repo.findByNormalizedName(
78
+ accountConfigId,
79
+ "nonexistent",
80
+ );
81
+ assert.equal(missing, null);
82
+ });
83
+
84
+ test("delete removes the row", async () => {
85
+ const accountConfigId = harness.makeId();
86
+ const label = await repo.create({ accountConfigId, name: "Gone" });
87
+
88
+ await repo.delete(accountConfigId, label.labelId);
89
+
90
+ await assert.rejects(repo.get(accountConfigId, label.labelId), (error) =>
91
+ harness.isNotFoundError(error),
92
+ );
93
+ });
94
+ });
95
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,75 @@
1
+ export class HTTPError extends Error {
2
+ public statusCode = 500;
3
+ }
4
+
5
+ export class BadRequestError extends HTTPError {
6
+ name = "BadRequestError";
7
+ public statusCode = 400;
8
+ }
9
+
10
+ export class ClientError extends HTTPError {
11
+ name = "ClientError";
12
+ public statusCode = 401;
13
+ }
14
+
15
+ export class ForbiddenError extends HTTPError {
16
+ name = "ForbiddenError";
17
+ public statusCode = 403;
18
+ }
19
+
20
+ export class NotFoundError extends HTTPError {
21
+ name = "NotFoundError";
22
+ public statusCode = 404;
23
+ }
24
+
25
+ export class ConflictError extends HTTPError {
26
+ name = "ConflictError";
27
+ public statusCode = 409;
28
+ }
29
+
30
+ /**
31
+ * A message exists but its body could not be fetched/parsed after every
32
+ * body-sync retry was spent (issue #1270 / epic #1281 invariant 3). This is
33
+ * distinct from `NotFoundError`: the message is real, but its content is
34
+ * permanently unavailable — the client must show an explicit, actionable
35
+ * error instead of retrying (no 202, no silent skip).
36
+ */
37
+ export class UnrecoverableBodyError extends HTTPError {
38
+ name = "UnrecoverableBodyError";
39
+ public statusCode = 422;
40
+ }
41
+
42
+ export class CreateFailedConflictError extends ConflictError {
43
+ public statusCode = 409;
44
+ name = "CreateFailedConflictError";
45
+ constructor(resourceType: string, params: unknown) {
46
+ super(
47
+ `${resourceType} with properties "${JSON.stringify(params)}" already exists.`,
48
+ );
49
+ }
50
+ }
51
+
52
+ export class UnhandledError extends HTTPError {
53
+ name = "UnhandledError";
54
+ public statusCode = 500;
55
+ readonly cause: Error | undefined;
56
+
57
+ constructor(message: string, cause?: Error) {
58
+ super(message);
59
+ this.message = message;
60
+ if (cause) this.cause = cause;
61
+ }
62
+
63
+ toJSON() {
64
+ return {
65
+ message: this.message,
66
+ cause: this.cause?.message,
67
+ stack: this.cause?.stack || this.stack,
68
+ };
69
+ }
70
+ }
71
+
72
+ export class InternalServerError extends UnhandledError {
73
+ name = "InternalServerError";
74
+ public statusCode = 500;
75
+ }
package/src/id.ts ADDED
@@ -0,0 +1,105 @@
1
+ import shortUuid from "short-uuid";
2
+ import { v5 as uuidv5 } from "uuid";
3
+ import type { MessageIdSource } from "./types.js";
4
+
5
+ const translator = shortUuid.createTranslator(shortUuid.constants.uuid25Base36);
6
+
7
+ export const base36uuid = (): string => translator.generate();
8
+
9
+ export const fromUUID = (uuid: string): string => translator.fromUUID(uuid);
10
+
11
+ export const toUUID = (shortId: string): string => translator.toUUID(shortId);
12
+
13
+ export const generateUuidv5 = (name: string, namespace: string): string =>
14
+ uuidv5(name, namespace);
15
+
16
+ export const base36uuidv5 = (name: string, namespace: string): string =>
17
+ translator.fromUUID(uuidv5(name, namespace));
18
+
19
+ export const REMIT_NAMESPACE = "9e89694d-214b-4d9b-99f5-214b4d9b99f5";
20
+
21
+ export const deriveAddressId = (
22
+ accountConfigId: string,
23
+ email: string,
24
+ ): string =>
25
+ base36uuidv5(
26
+ `address:${accountConfigId}:${email.toLowerCase()}`,
27
+ REMIT_NAMESPACE,
28
+ );
29
+
30
+ export const deriveEnvelopeAddressId = (
31
+ messageId: string,
32
+ role: string,
33
+ order: number,
34
+ ): string =>
35
+ base36uuidv5(
36
+ `envelopeaddress:${messageId}:${role}:${order}`,
37
+ REMIT_NAMESPACE,
38
+ );
39
+
40
+ export const isValidMessageId = (messageId: string | undefined): boolean => {
41
+ if (!messageId) return false;
42
+ const trimmed = messageId.trim();
43
+ return trimmed !== "" && trimmed !== "<>";
44
+ };
45
+
46
+ export const normalizeMessageIdHeader = (source: MessageIdSource): string => {
47
+ if (isValidMessageId(source.messageId)) {
48
+ return source.messageId as string;
49
+ }
50
+
51
+ const parts = [
52
+ "generated",
53
+ source.mailboxId,
54
+ source.uid.toString(),
55
+ source.date || "",
56
+ source.subject || "",
57
+ source.fromMailbox || "",
58
+ source.fromHost || "",
59
+ ];
60
+
61
+ return parts.join(":");
62
+ };
63
+
64
+ export const deriveMessageId = (
65
+ accountId: string,
66
+ messageIdHeader: string,
67
+ ): string =>
68
+ base36uuidv5(`message:${accountId}:${messageIdHeader}`, REMIT_NAMESPACE);
69
+
70
+ export const deriveMessageIdFromSource = (
71
+ accountId: string,
72
+ source: MessageIdSource,
73
+ ): string => deriveMessageId(accountId, normalizeMessageIdHeader(source));
74
+
75
+ export const deriveThreadId = (
76
+ accountId: string,
77
+ rootMessageIdHeader: string,
78
+ ): string =>
79
+ base36uuidv5(
80
+ `thread:${accountId}:${rootMessageIdHeader.toLowerCase()}`,
81
+ REMIT_NAMESPACE,
82
+ );
83
+
84
+ export const deriveEnvelopeId = (messageId: string): string =>
85
+ base36uuidv5(`envelope:${messageId}`, REMIT_NAMESPACE);
86
+
87
+ /**
88
+ * Canonical IMAP path for the root MIME node. BODYSTRUCTURE leaves the
89
+ * root's `part` blank; we assign "0" so it has a stable, unambiguous key
90
+ * across the codebase (BodyPart row id, S3 key, seed scripts).
91
+ */
92
+ export const ROOT_PART_PATH = "0";
93
+
94
+ export const deriveBodyPartId = (messageId: string, partPath: string): string =>
95
+ base36uuidv5(`bodypart:${messageId}:${partPath}`, REMIT_NAMESPACE);
96
+
97
+ export const deriveBodyPartParameterId = (
98
+ messageId: string,
99
+ partPath: string,
100
+ parameterName: string,
101
+ ): string =>
102
+ base36uuidv5(
103
+ `bodypartparam:${messageId}:${partPath}:${parameterName.toLowerCase()}`,
104
+ REMIT_NAMESPACE,
105
+ );
package/src/index.ts ADDED
@@ -0,0 +1,102 @@
1
+ export type { IAccountRepository } from "./interfaces/account.js";
2
+ export type { IAccountConfigRepository } from "./interfaces/account-config.js";
3
+ export type { IAccountExportRequestRepository } from "./interfaces/account-export-request.js";
4
+ export type { IAccountSettingRepository } from "./interfaces/account-setting.js";
5
+ export type { IAddressRepository } from "./interfaces/address.js";
6
+ export type { IEnvelopeRepository } from "./interfaces/envelope.js";
7
+ export type { IFilterRepository } from "./interfaces/filter.js";
8
+ export type { IFilterAnchorRepository } from "./interfaces/filter-anchor.js";
9
+ export type { ILabelRepository } from "./interfaces/label.js";
10
+ export type { IMailboxRepository } from "./interfaces/mailbox.js";
11
+ export type { IMailboxLockRepository } from "./interfaces/mailbox-lock.js";
12
+ export type { IMailboxSpecialUseRepository } from "./interfaces/mailbox-special-use.js";
13
+ export type { IMessageRepository } from "./interfaces/message.js";
14
+ export type { IMessageFlagRepository } from "./interfaces/message-flag.js";
15
+ export type { IMessageFlagPushRepository } from "./interfaces/message-flag-push.js";
16
+ export type { IMessageLabelRepository } from "./interfaces/message-label.js";
17
+ export type { IMessagePlacementMoveRepository } from "./interfaces/message-placement-move.js";
18
+ export type { IOrganizeJobRequestRepository } from "./interfaces/organize-job-request.js";
19
+ export type { IOutboxMessageRepository } from "./interfaces/outbox-message.js";
20
+ export type { IThreadMessageRepository } from "./interfaces/thread-message.js";
21
+ export type {
22
+ IUnitOfWork,
23
+ UnitOfWorkRepositories,
24
+ } from "./interfaces/unit-of-work.js";
25
+ export type {
26
+ AccountConfigDescription,
27
+ AccountConfigItem,
28
+ AccountDescription,
29
+ AccountExportRequestItem,
30
+ AccountItem,
31
+ AccountSchedulerPage,
32
+ AccountSettingItem,
33
+ AccountSettingValue,
34
+ AddressFlags,
35
+ AddressItem,
36
+ BodyPartContentItem,
37
+ BodyPartContentUpsertInput,
38
+ BodyPartItem,
39
+ BodyPartParameterItem,
40
+ BodyPartParameterUpsertInput,
41
+ BodyPartStorageItem,
42
+ BodyPartUpsertInput,
43
+ CreateAccountConfigInput,
44
+ CreateAccountExportRequestInput,
45
+ CreateAccountInput,
46
+ CreateAddressInput,
47
+ CreateEnvelopeAddressInput,
48
+ CreateEnvelopeInput,
49
+ CreateFilterAnchorInput,
50
+ CreateFilterInput,
51
+ CreateLabelInput,
52
+ CreateMailboxInput,
53
+ CreateMessageFlagInput,
54
+ CreateMessageInput,
55
+ CreateMessageLabelInput,
56
+ CreateOrganizeJobRequestInput,
57
+ CreateOutboxMessageInput,
58
+ CreateThreadMessageInput,
59
+ EnvelopeAddressItem,
60
+ EnvelopeItem,
61
+ FilterAnchorItem,
62
+ FilterItem,
63
+ FlagsMergePatch,
64
+ LabelItem,
65
+ ListOptions,
66
+ MailboxItem,
67
+ MailboxLockItem,
68
+ MailboxSpecialUseItem,
69
+ MailboxSpecialUseValue,
70
+ MessageData,
71
+ MessageDescription,
72
+ MessageFlagItem,
73
+ MessageFlagPushItem,
74
+ MessageIdSource,
75
+ MessageItem,
76
+ MessageLabelItem,
77
+ MessagePlacementMoveItem,
78
+ MessageReferenceItem,
79
+ OrganizeJobRequestItem,
80
+ OutboxMessageItem,
81
+ PutMessageFlagPushInput,
82
+ PutMessagePlacementMoveInput,
83
+ RawMessageStorageItem,
84
+ ResultList,
85
+ SearchOptions,
86
+ ThreadMessageItem,
87
+ UpdateAccountConfigInput,
88
+ UpdateAccountExportRequestInput,
89
+ UpdateAccountInput,
90
+ UpdateAddressInput,
91
+ UpdateEnvelopeInput,
92
+ UpdateFilterInput,
93
+ UpdateLabelInput,
94
+ UpdateMailboxInput,
95
+ UpdateMessageInput,
96
+ UpdateMessageMoveInput,
97
+ UpdateOrganizeJobRequestInput,
98
+ UpdateOutboxMessageInput,
99
+ UpdateThreadMessageInput,
100
+ UpsertAccountSettingInput,
101
+ WithMailboxLockResult,
102
+ } from "./types.js";
@@ -0,0 +1,25 @@
1
+ import type {
2
+ AccountConfigDescription,
3
+ AccountConfigItem,
4
+ CreateAccountConfigInput,
5
+ ResultList,
6
+ UpdateAccountConfigInput,
7
+ } from "../types.js";
8
+
9
+ export interface IAccountConfigRepository {
10
+ create(input: CreateAccountConfigInput): Promise<AccountConfigItem>;
11
+ get(accountConfigId: string): Promise<AccountConfigItem>;
12
+ get(accountConfigIds: string[]): Promise<AccountConfigItem[]>;
13
+ update(
14
+ accountConfigId: string,
15
+ input: UpdateAccountConfigInput,
16
+ ): Promise<AccountConfigItem>;
17
+ delete(accountConfigId: string): Promise<void>;
18
+ deleteMany(accountConfigIds: string[]): Promise<void>;
19
+ listByUser(
20
+ userId: string,
21
+ options?: { limit?: number; continuationToken?: string },
22
+ ): Promise<ResultList<AccountConfigItem>>;
23
+ describe(accountConfigId: string): Promise<AccountConfigDescription>;
24
+ listAll(): Promise<AccountConfigItem[]>;
25
+ }
@@ -0,0 +1,21 @@
1
+ import type {
2
+ AccountExportRequestItem,
3
+ CreateAccountExportRequestInput,
4
+ ResultList,
5
+ UpdateAccountExportRequestInput,
6
+ } from "../types.js";
7
+
8
+ export interface IAccountExportRequestRepository {
9
+ create(
10
+ input: CreateAccountExportRequestInput,
11
+ ): Promise<AccountExportRequestItem>;
12
+ get(accountExportRequestId: string): Promise<AccountExportRequestItem>;
13
+ update(
14
+ accountExportRequestId: string,
15
+ input: UpdateAccountExportRequestInput,
16
+ ): Promise<AccountExportRequestItem>;
17
+ listByAccountConfig(
18
+ accountConfigId: string,
19
+ options?: { limit?: number; continuationToken?: string },
20
+ ): Promise<ResultList<AccountExportRequestItem>>;
21
+ }