fluxmail 0.0.1 → 0.2.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 +37 -10
- package/dist/accounts/googleAuth.d.ts +31 -0
- package/dist/accounts/googleAuth.d.ts.map +1 -0
- package/dist/accounts/googleAuth.js +147 -0
- package/dist/accounts/googleAuth.js.map +1 -0
- package/dist/accounts/registry.d.ts +37 -0
- package/dist/accounts/registry.d.ts.map +1 -0
- package/dist/accounts/registry.js +278 -0
- package/dist/accounts/registry.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +629 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +216 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +15 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +15 -0
- package/dist/context.js.map +1 -0
- package/dist/http/app.d.ts +16 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +118 -0
- package/dist/http/app.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/licensing/activation.d.ts +5 -0
- package/dist/licensing/activation.d.ts.map +1 -0
- package/dist/licensing/activation.js +11 -0
- package/dist/licensing/activation.js.map +1 -0
- package/dist/licensing/client.d.ts +52 -0
- package/dist/licensing/client.d.ts.map +1 -0
- package/dist/licensing/client.js +75 -0
- package/dist/licensing/client.js.map +1 -0
- package/dist/licensing/entitlements.d.ts +55 -0
- package/dist/licensing/entitlements.d.ts.map +1 -0
- package/dist/licensing/entitlements.js +116 -0
- package/dist/licensing/entitlements.js.map +1 -0
- package/dist/licensing/lease.d.ts +47 -0
- package/dist/licensing/lease.d.ts.map +1 -0
- package/dist/licensing/lease.js +103 -0
- package/dist/licensing/lease.js.map +1 -0
- package/dist/licensing/refresher.d.ts +46 -0
- package/dist/licensing/refresher.d.ts.map +1 -0
- package/dist/licensing/refresher.js +150 -0
- package/dist/licensing/refresher.js.map +1 -0
- package/dist/mcp/buildServer.d.ts +28 -0
- package/dist/mcp/buildServer.d.ts.map +1 -0
- package/dist/mcp/buildServer.js +414 -0
- package/dist/mcp/buildServer.js.map +1 -0
- package/dist/scheduler/sendScheduler.d.ts +38 -0
- package/dist/scheduler/sendScheduler.d.ts.map +1 -0
- package/dist/scheduler/sendScheduler.js +128 -0
- package/dist/scheduler/sendScheduler.js.map +1 -0
- package/dist/service/emailService.d.ts +139 -0
- package/dist/service/emailService.d.ts.map +1 -0
- package/dist/service/emailService.js +380 -0
- package/dist/service/emailService.js.map +1 -0
- package/dist/storage/apiKeys.d.ts +27 -0
- package/dist/storage/apiKeys.d.ts.map +1 -0
- package/dist/storage/apiKeys.js +49 -0
- package/dist/storage/apiKeys.js.map +1 -0
- package/dist/storage/crypto.d.ts +4 -0
- package/dist/storage/crypto.d.ts.map +1 -0
- package/dist/storage/crypto.js +21 -0
- package/dist/storage/crypto.js.map +1 -0
- package/dist/storage/db.d.ts +1041 -0
- package/dist/storage/db.d.ts.map +1 -0
- package/dist/storage/db.js +211 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/imapState.d.ts +15 -0
- package/dist/storage/imapState.d.ts.map +1 -0
- package/dist/storage/imapState.js +89 -0
- package/dist/storage/imapState.js.map +1 -0
- package/dist/storage/members.d.ts +29 -0
- package/dist/storage/members.d.ts.map +1 -0
- package/dist/storage/members.js +67 -0
- package/dist/storage/members.js.map +1 -0
- package/dist/storage/scheduledSends.d.ts +60 -0
- package/dist/storage/scheduledSends.d.ts.map +1 -0
- package/dist/storage/scheduledSends.js +169 -0
- package/dist/storage/scheduledSends.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +49 -15
- package/bin/fluxmail.js +0 -4
- package/src/cli.js +0 -36
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/storage/db.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGjF,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASnB,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAapB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOtB,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAO7B,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBxB,CAAC;AAEF,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASlB,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMvB,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuB1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,qBAAqB,CAAC;AA6F/C,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CA0BjD"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
|
3
|
+
import { index, integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core';
|
|
4
|
+
export const members = sqliteTable('members', {
|
|
5
|
+
id: text('id').primaryKey(),
|
|
6
|
+
name: text('name').notNull(),
|
|
7
|
+
email: text('email'),
|
|
8
|
+
createdAt: integer('created_at').notNull(),
|
|
9
|
+
}, (table) => [uniqueIndex('members_email_unique').on(table.email)]);
|
|
10
|
+
export const accounts = sqliteTable('accounts', {
|
|
11
|
+
id: text('id').primaryKey(),
|
|
12
|
+
provider: text('provider').notNull(),
|
|
13
|
+
email: text('email').notNull(),
|
|
14
|
+
displayName: text('display_name'),
|
|
15
|
+
status: text('status').notNull().default('active'),
|
|
16
|
+
createdAt: integer('created_at').notNull(),
|
|
17
|
+
/** Owning member; NULL means the mailbox is shared across the instance. */
|
|
18
|
+
memberId: text('member_id').references(() => members.id, { onDelete: 'set null' }),
|
|
19
|
+
}, (table) => [uniqueIndex('accounts_provider_email_unique').on(table.provider, table.email)]);
|
|
20
|
+
export const oauthTokens = sqliteTable('oauth_tokens', {
|
|
21
|
+
accountId: text('account_id')
|
|
22
|
+
.primaryKey()
|
|
23
|
+
.references(() => accounts.id, { onDelete: 'cascade' }),
|
|
24
|
+
/** AES-256-GCM encrypted JSON blob of provider tokens. */
|
|
25
|
+
encryptedTokens: text('encrypted_tokens').notNull(),
|
|
26
|
+
updatedAt: integer('updated_at').notNull(),
|
|
27
|
+
});
|
|
28
|
+
export const accountCredentials = sqliteTable('account_credentials', {
|
|
29
|
+
accountId: text('account_id')
|
|
30
|
+
.primaryKey()
|
|
31
|
+
.references(() => accounts.id, { onDelete: 'cascade' }),
|
|
32
|
+
/** AES-256-GCM encrypted provider-specific credentials. */
|
|
33
|
+
encryptedCredentials: text('encrypted_credentials').notNull(),
|
|
34
|
+
updatedAt: integer('updated_at').notNull(),
|
|
35
|
+
});
|
|
36
|
+
export const imapMessages = sqliteTable('imap_messages', {
|
|
37
|
+
id: text('id').primaryKey(),
|
|
38
|
+
accountId: text('account_id')
|
|
39
|
+
.notNull()
|
|
40
|
+
.references(() => accounts.id, { onDelete: 'cascade' }),
|
|
41
|
+
mailboxPath: text('mailbox_path').notNull(),
|
|
42
|
+
uidValidity: text('uid_validity').notNull(),
|
|
43
|
+
uid: integer('uid').notNull(),
|
|
44
|
+
messageId: text('message_id'),
|
|
45
|
+
inReplyTo: text('in_reply_to'),
|
|
46
|
+
references: text('reference_ids'),
|
|
47
|
+
threadId: text('thread_id').notNull(),
|
|
48
|
+
draftId: text('draft_id'),
|
|
49
|
+
subject: text('subject'),
|
|
50
|
+
date: text('date'),
|
|
51
|
+
updatedAt: integer('updated_at').notNull(),
|
|
52
|
+
}, (table) => [
|
|
53
|
+
uniqueIndex('imap_messages_location_unique').on(table.accountId, table.mailboxPath, table.uidValidity, table.uid),
|
|
54
|
+
index('imap_messages_message_id').on(table.accountId, table.messageId),
|
|
55
|
+
index('imap_messages_thread_id').on(table.accountId, table.threadId),
|
|
56
|
+
uniqueIndex('imap_messages_draft_id_unique').on(table.accountId, table.draftId),
|
|
57
|
+
]);
|
|
58
|
+
export const apiKeys = sqliteTable('api_keys', {
|
|
59
|
+
id: text('id').primaryKey(),
|
|
60
|
+
name: text('name').notNull(),
|
|
61
|
+
/** SHA-256 hex of the key; the key itself is shown once at creation. */
|
|
62
|
+
keyHash: text('key_hash').notNull().unique(),
|
|
63
|
+
createdAt: integer('created_at').notNull(),
|
|
64
|
+
lastUsedAt: integer('last_used_at'),
|
|
65
|
+
/** Member the key was issued to; NULL means an unscoped admin key. */
|
|
66
|
+
memberId: text('member_id').references(() => members.id, { onDelete: 'set null' }),
|
|
67
|
+
});
|
|
68
|
+
export const licenseLease = sqliteTable('license_lease', {
|
|
69
|
+
/** Single-row table; the row id is always 'current'. */
|
|
70
|
+
id: text('id').primaryKey(),
|
|
71
|
+
/** Signed entitlement lease exactly as issued by the license server. */
|
|
72
|
+
token: text('token').notNull(),
|
|
73
|
+
updatedAt: integer('updated_at').notNull(),
|
|
74
|
+
});
|
|
75
|
+
export const scheduledSends = sqliteTable('scheduled_sends', {
|
|
76
|
+
id: text('id').primaryKey(),
|
|
77
|
+
accountId: text('account_id')
|
|
78
|
+
.notNull()
|
|
79
|
+
.references(() => accounts.id, { onDelete: 'cascade' }),
|
|
80
|
+
draftId: text('draft_id').notNull(),
|
|
81
|
+
/** Epoch milliseconds. */
|
|
82
|
+
sendAt: integer('send_at').notNull(),
|
|
83
|
+
createdAt: integer('created_at').notNull(),
|
|
84
|
+
status: text('status').notNull().default('pending'),
|
|
85
|
+
attempts: integer('attempts').notNull().default(0),
|
|
86
|
+
lastError: text('last_error'),
|
|
87
|
+
sentMessageId: text('sent_message_id'),
|
|
88
|
+
sentThreadId: text('sent_thread_id'),
|
|
89
|
+
/** Display snapshots taken at schedule time so listing never hits the provider. */
|
|
90
|
+
subject: text('subject'),
|
|
91
|
+
toRecipients: text('to_recipients'),
|
|
92
|
+
claimToken: text('claim_token'),
|
|
93
|
+
claimUntil: integer('claim_until'),
|
|
94
|
+
}, (table) => [index('scheduled_sends_pending_due').on(table.status, table.sendAt)]);
|
|
95
|
+
const BOOTSTRAP_SQL = `
|
|
96
|
+
CREATE TABLE IF NOT EXISTS members (
|
|
97
|
+
id TEXT PRIMARY KEY,
|
|
98
|
+
name TEXT NOT NULL,
|
|
99
|
+
email TEXT,
|
|
100
|
+
created_at INTEGER NOT NULL
|
|
101
|
+
);
|
|
102
|
+
CREATE UNIQUE INDEX IF NOT EXISTS members_email_unique ON members(email);
|
|
103
|
+
CREATE TABLE IF NOT EXISTS accounts (
|
|
104
|
+
id TEXT PRIMARY KEY,
|
|
105
|
+
provider TEXT NOT NULL,
|
|
106
|
+
email TEXT NOT NULL,
|
|
107
|
+
display_name TEXT,
|
|
108
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
109
|
+
created_at INTEGER NOT NULL,
|
|
110
|
+
member_id TEXT REFERENCES members(id) ON DELETE SET NULL
|
|
111
|
+
);
|
|
112
|
+
CREATE UNIQUE INDEX IF NOT EXISTS accounts_provider_email_unique
|
|
113
|
+
ON accounts(provider, email);
|
|
114
|
+
CREATE TABLE IF NOT EXISTS oauth_tokens (
|
|
115
|
+
account_id TEXT PRIMARY KEY REFERENCES accounts(id) ON DELETE CASCADE,
|
|
116
|
+
encrypted_tokens TEXT NOT NULL,
|
|
117
|
+
updated_at INTEGER NOT NULL
|
|
118
|
+
);
|
|
119
|
+
CREATE TABLE IF NOT EXISTS account_credentials (
|
|
120
|
+
account_id TEXT PRIMARY KEY REFERENCES accounts(id) ON DELETE CASCADE,
|
|
121
|
+
encrypted_credentials TEXT NOT NULL,
|
|
122
|
+
updated_at INTEGER NOT NULL
|
|
123
|
+
);
|
|
124
|
+
CREATE TABLE IF NOT EXISTS imap_messages (
|
|
125
|
+
id TEXT PRIMARY KEY,
|
|
126
|
+
account_id TEXT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
127
|
+
mailbox_path TEXT NOT NULL,
|
|
128
|
+
uid_validity TEXT NOT NULL,
|
|
129
|
+
uid INTEGER NOT NULL,
|
|
130
|
+
message_id TEXT,
|
|
131
|
+
in_reply_to TEXT,
|
|
132
|
+
reference_ids TEXT,
|
|
133
|
+
thread_id TEXT NOT NULL,
|
|
134
|
+
draft_id TEXT,
|
|
135
|
+
subject TEXT,
|
|
136
|
+
date TEXT,
|
|
137
|
+
updated_at INTEGER NOT NULL
|
|
138
|
+
);
|
|
139
|
+
CREATE UNIQUE INDEX IF NOT EXISTS imap_messages_location_unique
|
|
140
|
+
ON imap_messages(account_id, mailbox_path, uid_validity, uid);
|
|
141
|
+
CREATE INDEX IF NOT EXISTS imap_messages_message_id
|
|
142
|
+
ON imap_messages(account_id, message_id);
|
|
143
|
+
CREATE INDEX IF NOT EXISTS imap_messages_thread_id
|
|
144
|
+
ON imap_messages(account_id, thread_id);
|
|
145
|
+
CREATE UNIQUE INDEX IF NOT EXISTS imap_messages_draft_id_unique
|
|
146
|
+
ON imap_messages(account_id, draft_id);
|
|
147
|
+
CREATE TABLE IF NOT EXISTS api_keys (
|
|
148
|
+
id TEXT PRIMARY KEY,
|
|
149
|
+
name TEXT NOT NULL,
|
|
150
|
+
key_hash TEXT NOT NULL UNIQUE,
|
|
151
|
+
created_at INTEGER NOT NULL,
|
|
152
|
+
last_used_at INTEGER,
|
|
153
|
+
member_id TEXT REFERENCES members(id) ON DELETE SET NULL
|
|
154
|
+
);
|
|
155
|
+
CREATE TABLE IF NOT EXISTS license_lease (
|
|
156
|
+
id TEXT PRIMARY KEY,
|
|
157
|
+
token TEXT NOT NULL,
|
|
158
|
+
updated_at INTEGER NOT NULL
|
|
159
|
+
);
|
|
160
|
+
CREATE TABLE IF NOT EXISTS scheduled_sends (
|
|
161
|
+
id TEXT PRIMARY KEY,
|
|
162
|
+
account_id TEXT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
163
|
+
draft_id TEXT NOT NULL,
|
|
164
|
+
send_at INTEGER NOT NULL,
|
|
165
|
+
created_at INTEGER NOT NULL,
|
|
166
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
167
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
168
|
+
last_error TEXT,
|
|
169
|
+
sent_message_id TEXT,
|
|
170
|
+
sent_thread_id TEXT,
|
|
171
|
+
subject TEXT,
|
|
172
|
+
to_recipients TEXT,
|
|
173
|
+
claim_token TEXT,
|
|
174
|
+
claim_until INTEGER
|
|
175
|
+
);
|
|
176
|
+
CREATE INDEX IF NOT EXISTS scheduled_sends_pending_due
|
|
177
|
+
ON scheduled_sends(status, send_at);
|
|
178
|
+
`;
|
|
179
|
+
function tableColumns(sqlite, table) {
|
|
180
|
+
return new Set(sqlite.prepare(`PRAGMA table_info(${table})`).all().map((column) => column.name));
|
|
181
|
+
}
|
|
182
|
+
export function openDb(dbPath) {
|
|
183
|
+
const sqlite = new Database(dbPath);
|
|
184
|
+
sqlite.pragma('journal_mode = WAL');
|
|
185
|
+
sqlite.pragma('foreign_keys = ON');
|
|
186
|
+
sqlite.exec(BOOTSTRAP_SQL);
|
|
187
|
+
// Keep the generic credential row in sync if an older binary updated the
|
|
188
|
+
// legacy Gmail token table before this version reopened the database.
|
|
189
|
+
sqlite.exec(`
|
|
190
|
+
INSERT INTO account_credentials (account_id, encrypted_credentials, updated_at)
|
|
191
|
+
SELECT account_id, encrypted_tokens, updated_at FROM oauth_tokens WHERE true
|
|
192
|
+
ON CONFLICT(account_id) DO UPDATE SET
|
|
193
|
+
encrypted_credentials = excluded.encrypted_credentials,
|
|
194
|
+
updated_at = excluded.updated_at
|
|
195
|
+
WHERE excluded.updated_at > account_credentials.updated_at
|
|
196
|
+
`);
|
|
197
|
+
const scheduledCols = tableColumns(sqlite, 'scheduled_sends');
|
|
198
|
+
if (!scheduledCols.has('claim_token'))
|
|
199
|
+
sqlite.exec('ALTER TABLE scheduled_sends ADD COLUMN claim_token TEXT');
|
|
200
|
+
if (!scheduledCols.has('claim_until'))
|
|
201
|
+
sqlite.exec('ALTER TABLE scheduled_sends ADD COLUMN claim_until INTEGER');
|
|
202
|
+
// Pre-members databases: NULL means shared mailbox / unscoped key, so old rows need no backfill.
|
|
203
|
+
if (!tableColumns(sqlite, 'accounts').has('member_id')) {
|
|
204
|
+
sqlite.exec('ALTER TABLE accounts ADD COLUMN member_id TEXT REFERENCES members(id) ON DELETE SET NULL');
|
|
205
|
+
}
|
|
206
|
+
if (!tableColumns(sqlite, 'api_keys').has('member_id')) {
|
|
207
|
+
sqlite.exec('ALTER TABLE api_keys ADD COLUMN member_id TEXT REFERENCES members(id) ON DELETE SET NULL');
|
|
208
|
+
}
|
|
209
|
+
return drizzle(sqlite);
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/storage/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ImapMessageLocation, ImapStateStore } from '@fluxmail/provider-imap';
|
|
2
|
+
import { type FluxmailDb } from './db.js';
|
|
3
|
+
export declare class SqliteImapStateStore implements ImapStateStore {
|
|
4
|
+
private readonly db;
|
|
5
|
+
private readonly accountId;
|
|
6
|
+
constructor(db: FluxmailDb, accountId: string);
|
|
7
|
+
findById(id: string): Promise<ImapMessageLocation | undefined>;
|
|
8
|
+
findByLocation(path: string, uidValidity: string, uid: number): Promise<ImapMessageLocation | undefined>;
|
|
9
|
+
findByDraftId(draftId: string): Promise<ImapMessageLocation | undefined>;
|
|
10
|
+
listByThreadId(threadId: string): Promise<ImapMessageLocation[]>;
|
|
11
|
+
save(location: ImapMessageLocation): Promise<void>;
|
|
12
|
+
remove(id: string): Promise<void>;
|
|
13
|
+
invalidateMailbox(path: string, uidValidity: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=imapState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imapState.d.ts","sourceRoot":"","sources":["../../src/storage/imapState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAmBxD,qBAAa,oBAAqB,YAAW,cAAc;IAEvD,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,EAAE,EAAE,UAAU,EACd,SAAS,EAAE,MAAM;IAG9B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAS9D,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAgBxG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IASxE,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAShE,IAAI,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAY1E"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { and, eq, ne } from 'drizzle-orm';
|
|
2
|
+
import { imapMessages } from './db.js';
|
|
3
|
+
function fromRow(row) {
|
|
4
|
+
return {
|
|
5
|
+
id: row.id,
|
|
6
|
+
accountId: row.accountId,
|
|
7
|
+
mailboxPath: row.mailboxPath,
|
|
8
|
+
uidValidity: row.uidValidity,
|
|
9
|
+
uid: row.uid,
|
|
10
|
+
...(row.messageId ? { messageId: row.messageId } : {}),
|
|
11
|
+
...(row.inReplyTo ? { inReplyTo: row.inReplyTo } : {}),
|
|
12
|
+
...(row.references ? { references: JSON.parse(row.references) } : {}),
|
|
13
|
+
threadId: row.threadId,
|
|
14
|
+
...(row.draftId ? { draftId: row.draftId } : {}),
|
|
15
|
+
...(row.subject !== null ? { subject: row.subject } : {}),
|
|
16
|
+
...(row.date !== null ? { date: row.date } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export class SqliteImapStateStore {
|
|
20
|
+
db;
|
|
21
|
+
accountId;
|
|
22
|
+
constructor(db, accountId) {
|
|
23
|
+
this.db = db;
|
|
24
|
+
this.accountId = accountId;
|
|
25
|
+
}
|
|
26
|
+
async findById(id) {
|
|
27
|
+
const row = this.db
|
|
28
|
+
.select()
|
|
29
|
+
.from(imapMessages)
|
|
30
|
+
.where(and(eq(imapMessages.accountId, this.accountId), eq(imapMessages.id, id)))
|
|
31
|
+
.get();
|
|
32
|
+
return row ? fromRow(row) : undefined;
|
|
33
|
+
}
|
|
34
|
+
async findByLocation(path, uidValidity, uid) {
|
|
35
|
+
const row = this.db
|
|
36
|
+
.select()
|
|
37
|
+
.from(imapMessages)
|
|
38
|
+
.where(and(eq(imapMessages.accountId, this.accountId), eq(imapMessages.mailboxPath, path), eq(imapMessages.uidValidity, uidValidity), eq(imapMessages.uid, uid)))
|
|
39
|
+
.get();
|
|
40
|
+
return row ? fromRow(row) : undefined;
|
|
41
|
+
}
|
|
42
|
+
async findByDraftId(draftId) {
|
|
43
|
+
const row = this.db
|
|
44
|
+
.select()
|
|
45
|
+
.from(imapMessages)
|
|
46
|
+
.where(and(eq(imapMessages.accountId, this.accountId), eq(imapMessages.draftId, draftId)))
|
|
47
|
+
.get();
|
|
48
|
+
return row ? fromRow(row) : undefined;
|
|
49
|
+
}
|
|
50
|
+
async listByThreadId(threadId) {
|
|
51
|
+
return this.db
|
|
52
|
+
.select()
|
|
53
|
+
.from(imapMessages)
|
|
54
|
+
.where(and(eq(imapMessages.accountId, this.accountId), eq(imapMessages.threadId, threadId)))
|
|
55
|
+
.all()
|
|
56
|
+
.map(fromRow);
|
|
57
|
+
}
|
|
58
|
+
async save(location) {
|
|
59
|
+
const values = {
|
|
60
|
+
id: location.id,
|
|
61
|
+
accountId: this.accountId,
|
|
62
|
+
mailboxPath: location.mailboxPath,
|
|
63
|
+
uidValidity: location.uidValidity,
|
|
64
|
+
uid: location.uid,
|
|
65
|
+
messageId: location.messageId ?? null,
|
|
66
|
+
inReplyTo: location.inReplyTo ?? null,
|
|
67
|
+
references: location.references ? JSON.stringify(location.references) : null,
|
|
68
|
+
threadId: location.threadId,
|
|
69
|
+
draftId: location.draftId ?? null,
|
|
70
|
+
subject: location.subject ?? null,
|
|
71
|
+
date: location.date ?? null,
|
|
72
|
+
updatedAt: Date.now(),
|
|
73
|
+
};
|
|
74
|
+
this.db.insert(imapMessages).values(values).onConflictDoUpdate({ target: imapMessages.id, set: values }).run();
|
|
75
|
+
}
|
|
76
|
+
async remove(id) {
|
|
77
|
+
this.db
|
|
78
|
+
.delete(imapMessages)
|
|
79
|
+
.where(and(eq(imapMessages.accountId, this.accountId), eq(imapMessages.id, id)))
|
|
80
|
+
.run();
|
|
81
|
+
}
|
|
82
|
+
async invalidateMailbox(path, uidValidity) {
|
|
83
|
+
this.db
|
|
84
|
+
.delete(imapMessages)
|
|
85
|
+
.where(and(eq(imapMessages.accountId, this.accountId), eq(imapMessages.mailboxPath, path), ne(imapMessages.uidValidity, uidValidity)))
|
|
86
|
+
.run();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=imapState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imapState.js","sourceRoot":"","sources":["../../src/storage/imapState.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type FluxmailDb } from './db.js';
|
|
2
|
+
export interface MemberInfo {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
email: string | null;
|
|
6
|
+
createdAt: number;
|
|
7
|
+
/** Mailboxes owned by this member (shared mailboxes are not counted). */
|
|
8
|
+
accountCount: number;
|
|
9
|
+
apiKeyCount: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function addMember(db: FluxmailDb, input: {
|
|
12
|
+
name: string;
|
|
13
|
+
email?: string;
|
|
14
|
+
}): MemberInfo;
|
|
15
|
+
export declare function getMember(db: FluxmailDb, id: string): MemberInfo;
|
|
16
|
+
/** Resolve a member by id or email, for CLI --member flags. */
|
|
17
|
+
export declare function findMember(db: FluxmailDb, ref: string): MemberInfo;
|
|
18
|
+
export declare function listMembers(db: FluxmailDb): MemberInfo[];
|
|
19
|
+
/**
|
|
20
|
+
* Delete a member. Their mailboxes become shared (member_id is set to NULL by
|
|
21
|
+
* the foreign key), and their API keys are revoked: a key must never outlive
|
|
22
|
+
* the member scoping it, or it would fall back to unscoped admin access.
|
|
23
|
+
*/
|
|
24
|
+
export declare function removeMember(db: FluxmailDb, id: string): {
|
|
25
|
+
name: string;
|
|
26
|
+
freedAccounts: number;
|
|
27
|
+
revokedApiKeys: number;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=members.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../../src/storage/members.ts"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAGtE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,SAAS,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAgB7F;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAIhE;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAMlE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,UAAU,GAAG,UAAU,EAAE,CAMxD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,MAAM,GACT;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAOjE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { eq } from 'drizzle-orm';
|
|
3
|
+
import { EmailError } from '@fluxmail/core';
|
|
4
|
+
import { accounts, apiKeys, members } from './db.js';
|
|
5
|
+
import { assertMemberLimit, getEntitlements } from '../licensing/entitlements.js';
|
|
6
|
+
function toInfo(db, row) {
|
|
7
|
+
return {
|
|
8
|
+
...row,
|
|
9
|
+
accountCount: db.select().from(accounts).where(eq(accounts.memberId, row.id)).all().length,
|
|
10
|
+
apiKeyCount: db.select().from(apiKeys).where(eq(apiKeys.memberId, row.id)).all().length,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function addMember(db, input) {
|
|
14
|
+
const name = input.name.trim();
|
|
15
|
+
if (!name)
|
|
16
|
+
throw new EmailError('invalid_request', 'Member name cannot be empty');
|
|
17
|
+
const email = input.email?.trim().toLowerCase();
|
|
18
|
+
const id = `member_${randomBytes(6).toString('hex')}`;
|
|
19
|
+
db.transaction((tx) => {
|
|
20
|
+
if (email && tx.select().from(members).where(eq(members.email, email)).get()) {
|
|
21
|
+
throw new EmailError('invalid_request', `A member with email ${email} already exists`);
|
|
22
|
+
}
|
|
23
|
+
const memberCount = tx.select().from(members).all().length;
|
|
24
|
+
assertMemberLimit(memberCount, getEntitlements(tx));
|
|
25
|
+
tx.insert(members)
|
|
26
|
+
.values({ id, name, email: email ?? null, createdAt: Date.now() })
|
|
27
|
+
.run();
|
|
28
|
+
});
|
|
29
|
+
return getMember(db, id);
|
|
30
|
+
}
|
|
31
|
+
export function getMember(db, id) {
|
|
32
|
+
const row = db.select().from(members).where(eq(members.id, id)).get();
|
|
33
|
+
if (!row)
|
|
34
|
+
throw new EmailError('not_found', `No member with id "${id}"`);
|
|
35
|
+
return toInfo(db, row);
|
|
36
|
+
}
|
|
37
|
+
/** Resolve a member by id or email, for CLI --member flags. */
|
|
38
|
+
export function findMember(db, ref) {
|
|
39
|
+
const byId = db.select().from(members).where(eq(members.id, ref)).get();
|
|
40
|
+
if (byId)
|
|
41
|
+
return toInfo(db, byId);
|
|
42
|
+
const byEmail = db.select().from(members).where(eq(members.email, ref.trim().toLowerCase())).get();
|
|
43
|
+
if (byEmail)
|
|
44
|
+
return toInfo(db, byEmail);
|
|
45
|
+
throw new EmailError('not_found', `No member with id or email "${ref}". Run "fluxmail members list" to see members.`);
|
|
46
|
+
}
|
|
47
|
+
export function listMembers(db) {
|
|
48
|
+
return db
|
|
49
|
+
.select()
|
|
50
|
+
.from(members)
|
|
51
|
+
.all()
|
|
52
|
+
.map((row) => toInfo(db, row));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Delete a member. Their mailboxes become shared (member_id is set to NULL by
|
|
56
|
+
* the foreign key), and their API keys are revoked: a key must never outlive
|
|
57
|
+
* the member scoping it, or it would fall back to unscoped admin access.
|
|
58
|
+
*/
|
|
59
|
+
export function removeMember(db, id) {
|
|
60
|
+
const info = getMember(db, id);
|
|
61
|
+
db.transaction((tx) => {
|
|
62
|
+
tx.delete(apiKeys).where(eq(apiKeys.memberId, id)).run();
|
|
63
|
+
tx.delete(members).where(eq(members.id, id)).run();
|
|
64
|
+
});
|
|
65
|
+
return { name: info.name, freedAccounts: info.accountCount, revokedApiKeys: info.apiKeyCount };
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=members.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"members.js","sourceRoot":"","sources":["../../src/storage/members.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type FluxmailDb } from './db.js';
|
|
2
|
+
export type ScheduledSendStatus = 'pending' | 'sending' | 'sent' | 'failed' | 'canceled';
|
|
3
|
+
export interface ScheduledSendRow {
|
|
4
|
+
id: string;
|
|
5
|
+
accountId: string;
|
|
6
|
+
draftId: string;
|
|
7
|
+
/** Epoch milliseconds. */
|
|
8
|
+
sendAt: number;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
status: ScheduledSendStatus;
|
|
11
|
+
attempts: number;
|
|
12
|
+
lastError: string | null;
|
|
13
|
+
sentMessageId: string | null;
|
|
14
|
+
sentThreadId: string | null;
|
|
15
|
+
subject: string | null;
|
|
16
|
+
toRecipients: string | null;
|
|
17
|
+
claimToken: string | null;
|
|
18
|
+
claimUntil: number | null;
|
|
19
|
+
}
|
|
20
|
+
export declare function createScheduledSend(db: FluxmailDb, input: {
|
|
21
|
+
accountId: string;
|
|
22
|
+
draftId: string;
|
|
23
|
+
sendAt: number;
|
|
24
|
+
subject?: string;
|
|
25
|
+
toRecipients?: string;
|
|
26
|
+
}): ScheduledSendRow;
|
|
27
|
+
export declare function getScheduledSend(db: FluxmailDb, id: string): ScheduledSendRow | undefined;
|
|
28
|
+
/** All schedules, pending first by due time, then the rest newest-first. */
|
|
29
|
+
export declare function listScheduledSends(db: FluxmailDb, accountId?: string): ScheduledSendRow[];
|
|
30
|
+
export declare function listPending(db: FluxmailDb): ScheduledSendRow[];
|
|
31
|
+
export declare function listActive(db: FluxmailDb): ScheduledSendRow[];
|
|
32
|
+
export declare function findPendingByDraft(db: FluxmailDb, accountId: string, draftId: string): ScheduledSendRow | undefined;
|
|
33
|
+
export declare function markSent(db: FluxmailDb, id: string, result: {
|
|
34
|
+
id: string;
|
|
35
|
+
threadId: string;
|
|
36
|
+
}): void;
|
|
37
|
+
/** Atomically claim a due row so only one scheduler process can dispatch it. */
|
|
38
|
+
export declare function claimScheduledSend(db: FluxmailDb, id: string, now: number, leaseMs: number): {
|
|
39
|
+
row: ScheduledSendRow;
|
|
40
|
+
token: string;
|
|
41
|
+
} | undefined;
|
|
42
|
+
export declare function completeClaim(db: FluxmailDb, id: string, token: string, result: {
|
|
43
|
+
id: string;
|
|
44
|
+
threadId: string;
|
|
45
|
+
}): void;
|
|
46
|
+
export declare function failClaim(db: FluxmailDb, id: string, token: string, error: string): void;
|
|
47
|
+
export declare function retryClaim(db: FluxmailDb, id: string, token: string, error: string): void;
|
|
48
|
+
export declare function listClaimable(db: FluxmailDb, now: number): ScheduledSendRow[];
|
|
49
|
+
export declare function markFailed(db: FluxmailDb, id: string, error: string): void;
|
|
50
|
+
/** Record why a pending send is held (e.g. plan quota) without consuming an attempt. */
|
|
51
|
+
export declare function holdPending(db: FluxmailDb, id: string, error: string): void;
|
|
52
|
+
/** Record a retryable failure: bump attempts, keep the row pending. */
|
|
53
|
+
export declare function recordAttempt(db: FluxmailDb, id: string, error: string): void;
|
|
54
|
+
/** Only pending schedules can be canceled; returns false otherwise. */
|
|
55
|
+
export declare function cancelScheduledSend(db: FluxmailDb, id: string): boolean;
|
|
56
|
+
export declare function countPending(db: FluxmailDb): {
|
|
57
|
+
pending: number;
|
|
58
|
+
nextSendAt?: number;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=scheduledSends.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduledSends.d.ts","sourceRoot":"","sources":["../../src/storage/scheduledSends.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1D,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAQD,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,UAAU,EACd,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACrG,gBAAgB,CAwClB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAGzF;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAQzF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,UAAU,GAAG,gBAAgB,EAAE,CAE9D;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,UAAU,GAAG,gBAAgB,EAAE,CAO7D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAanH;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKnG;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd;IAAE,GAAG,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAiBtD;AAED,wBAAgB,aAAa,CAC3B,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvC,IAAI,CAYN;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAKxF;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAazF;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAY7E;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAE1E;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAK3E;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAO7E;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAOvE;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAIrF"}
|