@remit/drizzle-service 0.0.101 → 0.0.103
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 +1 -1
- package/src/batch.sqlite.test.ts +4 -4
- package/src/batch.ts +47 -21
- package/src/databases.ts +24 -6
- package/src/db.ts +20 -5
- package/src/index.ts +12 -0
- package/src/repair/junk-only-address.sqlite.test.ts +7 -2
- package/src/repos/account.conformance.sqlite.test.ts +6 -2
- package/src/repos/calendar-collection.ts +2 -2
- package/src/repos/calendar-default.sqlite.test.ts +6 -2
- package/src/repos/calendar-event-index.ts +2 -2
- package/src/repos/calendar-feed-token.ts +2 -2
- package/src/repos/calendar-object.ts +2 -2
- package/src/repos/calendar-put.sqlite.test.ts +6 -2
- package/src/repos/calendar-store.ts +36 -40
- package/src/repos/calendar-suggestion.ts +5 -4
- package/src/repos/cascade-delete.test.ts +3 -2
- package/src/repos/cascade-delete.ts +38 -35
- package/src/repos/envelope.sqlite.test.ts +3 -2
- package/src/repos/envelope.test.ts +3 -2
- package/src/repos/envelope.ts +19 -13
- package/src/repos/filter.ts +2 -2
- package/src/repos/i4-account-config.test.ts +10 -6
- package/src/repos/i4-account-config.ts +12 -13
- package/src/repos/i4-account-export-request.ts +2 -2
- package/src/repos/i4-account-setting.ts +6 -5
- package/src/repos/i4-account.test.ts +12 -8
- package/src/repos/i4-account.ts +10 -13
- package/src/repos/i4-address-junk-move.sqlite.test.ts +7 -2
- package/src/repos/i4-address.ts +17 -7
- package/src/repos/i4-config-import.ts +2 -2
- package/src/repos/i4-mailbox-lock.ts +2 -2
- package/src/repos/i4-mailbox-special-use.sqlite.test.ts +12 -10
- package/src/repos/i4-mailbox-special-use.ts +34 -44
- package/src/repos/i4-mailbox.sqlite.test.ts +12 -10
- package/src/repos/i4-mailbox.test.ts +15 -14
- package/src/repos/i4-mailbox.ts +19 -12
- package/src/repos/i4-message-flag-push.ts +2 -2
- package/src/repos/i4-message-placement-move.ts +2 -2
- package/src/repos/i4-organize-job-request.ts +2 -2
- package/src/repos/i4-outbox-attachment.test.ts +7 -2
- package/src/repos/i4-outbox-attachment.ts +14 -10
- package/src/repos/i4-outbox-message.ts +7 -6
- package/src/repos/i4-sender-signer-standing.ts +2 -2
- package/src/repos/in-list.sqlite.test.ts +61 -0
- package/src/repos/in-list.ts +14 -0
- package/src/repos/label.ts +2 -2
- package/src/repos/message-flag.ts +8 -10
- package/src/repos/message-label.ts +5 -4
- package/src/repos/message.sqlite.test.ts +3 -3
- package/src/repos/message.test.ts +6 -5
- package/src/repos/message.ts +30 -24
- package/src/repos/quarantine.ts +2 -2
- package/src/repos/sync-warning.ts +5 -4
- package/src/repos/thread-message-body-muted.sqlite.test.ts +7 -3
- package/src/repos/thread-message-field-terms.sqlite.test.ts +7 -6
- package/src/repos/thread-message.sqlite.test.ts +7 -3
- package/src/repos/thread-message.test.ts +22 -18
- package/src/repos/thread-message.ts +16 -7
- package/src/search/thread-message-search.sqlite.test.ts +8 -3
- package/src/search/thread-message-search.ts +13 -8
- package/src/sqlite-repositories.ts +48 -32
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
type SQLWrapper,
|
|
24
24
|
sql,
|
|
25
25
|
} from "drizzle-orm";
|
|
26
|
-
import {
|
|
27
|
-
import type {
|
|
26
|
+
import type { WriteBatch } from "../batch.js";
|
|
27
|
+
import type { RepositoryDb, SqliteStore } from "../db.js";
|
|
28
28
|
import { NotFoundError } from "../error.js";
|
|
29
29
|
import { deterministicBase36Id } from "../id.js";
|
|
30
30
|
import { decodeToken } from "../pagination.js";
|
|
@@ -34,6 +34,7 @@ import type {
|
|
|
34
34
|
SearchIndex,
|
|
35
35
|
SearchPosition,
|
|
36
36
|
} from "../search/thread-message-search.js";
|
|
37
|
+
import { inList } from "./in-list.js";
|
|
37
38
|
import {
|
|
38
39
|
bodyMatch,
|
|
39
40
|
fromMatch,
|
|
@@ -43,6 +44,8 @@ import {
|
|
|
43
44
|
subjectMatch,
|
|
44
45
|
} from "./thread-search-predicates.js";
|
|
45
46
|
|
|
47
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
48
|
+
|
|
46
49
|
export const deriveThreadMessageId = (
|
|
47
50
|
threadId: string,
|
|
48
51
|
messageId: string,
|
|
@@ -325,10 +328,16 @@ function sentDateCursorCond(
|
|
|
325
328
|
export class DrizzleThreadMessageRepository
|
|
326
329
|
implements IThreadMessageRepository
|
|
327
330
|
{
|
|
331
|
+
private readonly db: DB;
|
|
332
|
+
private readonly batch: WriteBatch;
|
|
333
|
+
|
|
328
334
|
constructor(
|
|
329
|
-
|
|
335
|
+
store: SqliteStore<Record<string, unknown>>,
|
|
330
336
|
private readonly search: SearchIndex,
|
|
331
|
-
) {
|
|
337
|
+
) {
|
|
338
|
+
this.db = store.db;
|
|
339
|
+
this.batch = store.batch;
|
|
340
|
+
}
|
|
332
341
|
|
|
333
342
|
/**
|
|
334
343
|
* One page of rows in date order (ranked first when asked). Without a text
|
|
@@ -579,7 +588,7 @@ export class DrizzleThreadMessageRepository
|
|
|
579
588
|
.where(
|
|
580
589
|
and(
|
|
581
590
|
eq(threadMessageTable.accountConfigId, accountConfigId),
|
|
582
|
-
|
|
591
|
+
inList(threadMessageTable.threadMessageId, threadMessageId),
|
|
583
592
|
),
|
|
584
593
|
);
|
|
585
594
|
return rows.map(toItem);
|
|
@@ -678,7 +687,7 @@ export class DrizzleThreadMessageRepository
|
|
|
678
687
|
.where(
|
|
679
688
|
and(
|
|
680
689
|
eq(threadMessageTable.accountConfigId, accountConfigId),
|
|
681
|
-
|
|
690
|
+
inList(threadMessageTable.threadMessageId, ids),
|
|
682
691
|
),
|
|
683
692
|
);
|
|
684
693
|
await this.search.sync(ids);
|
|
@@ -1081,7 +1090,7 @@ export class DrizzleThreadMessageRepository
|
|
|
1081
1090
|
.returning({ threadMessageId: threadMessageTable.threadMessageId }),
|
|
1082
1091
|
];
|
|
1083
1092
|
});
|
|
1084
|
-
const results = await
|
|
1093
|
+
const results = await this.batch(items);
|
|
1085
1094
|
await this.search.sync(
|
|
1086
1095
|
rows.flatMap((row) => [
|
|
1087
1096
|
row.threadMessageId,
|
|
@@ -3,8 +3,9 @@ import { after, before, describe, test } from "node:test";
|
|
|
3
3
|
import type { CreateThreadMessageInput } from "@remit/data-ports";
|
|
4
4
|
import Database from "better-sqlite3";
|
|
5
5
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
6
|
+
import { syncStore } from "../databases.js";
|
|
6
7
|
import type { Db } from "../db.js";
|
|
7
|
-
import {
|
|
8
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
8
9
|
import { applyMigrationSet } from "../test-shipped-sqlite-schema.js";
|
|
9
10
|
import {
|
|
10
11
|
SearchIndex,
|
|
@@ -48,8 +49,12 @@ const open = (set: "mail" | "search") => {
|
|
|
48
49
|
describe("the thread-message search index in its own database", () => {
|
|
49
50
|
const mail = open("mail");
|
|
50
51
|
const search = open("search");
|
|
51
|
-
const index = new SearchIndex(mail.db, search.db);
|
|
52
|
-
const repo =
|
|
52
|
+
const index = new SearchIndex(mail.db, syncStore(search.db));
|
|
53
|
+
const repo = createSqliteRepositories({
|
|
54
|
+
app: mail.db,
|
|
55
|
+
mail: mail.db,
|
|
56
|
+
search: search.db,
|
|
57
|
+
}).threadMessage;
|
|
53
58
|
|
|
54
59
|
const found = async (query: string): Promise<string[]> =>
|
|
55
60
|
(await repo.searchByMailboxWindow(ACCOUNT, MAILBOX, { query })).items.map(
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
sql,
|
|
13
13
|
} from "drizzle-orm";
|
|
14
14
|
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
15
|
-
import {
|
|
16
|
-
import type {
|
|
15
|
+
import type { SqliteBatchItem, WriteBatch } from "../batch.js";
|
|
16
|
+
import type { RepositoryDb, SqliteStore } from "../db.js";
|
|
17
17
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
18
18
|
|
|
19
19
|
// The FTS5 trigram table the search migration set creates
|
|
@@ -32,7 +32,7 @@ export const threadMessageSearchTable = sqliteTable("thread_message_fts", {
|
|
|
32
32
|
body: text("body").notNull(),
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
type AnyDb =
|
|
35
|
+
type AnyDb = RepositoryDb<Record<string, unknown>>;
|
|
36
36
|
|
|
37
37
|
type SearchDocument = typeof threadMessageSearchTable.$inferInsert;
|
|
38
38
|
|
|
@@ -138,10 +138,16 @@ export interface CandidateQuery {
|
|
|
138
138
|
* only, so no statement carries more ids than a page holds.
|
|
139
139
|
*/
|
|
140
140
|
export class SearchIndex {
|
|
141
|
+
private readonly search: AnyDb;
|
|
142
|
+
private readonly batch: WriteBatch;
|
|
143
|
+
|
|
141
144
|
constructor(
|
|
142
145
|
private readonly mail: AnyDb,
|
|
143
|
-
|
|
144
|
-
) {
|
|
146
|
+
store: SqliteStore<Record<string, unknown>>,
|
|
147
|
+
) {
|
|
148
|
+
this.search = store.db;
|
|
149
|
+
this.batch = store.batch;
|
|
150
|
+
}
|
|
145
151
|
|
|
146
152
|
/** Make the index match the mail database for these thread messages. */
|
|
147
153
|
async sync(threadMessageIds: readonly string[]): Promise<void> {
|
|
@@ -165,7 +171,7 @@ export class SearchIndex {
|
|
|
165
171
|
.values(rows.map(toDocument)),
|
|
166
172
|
);
|
|
167
173
|
}
|
|
168
|
-
await
|
|
174
|
+
await this.batch(items);
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
177
|
|
|
@@ -256,8 +262,7 @@ export class SearchIndex {
|
|
|
256
262
|
.limit(DOCUMENTS_PER_REBUILD_PAGE);
|
|
257
263
|
const last = rows.at(-1);
|
|
258
264
|
if (!last) return indexed;
|
|
259
|
-
await
|
|
260
|
-
this.search,
|
|
265
|
+
await this.batch(
|
|
261
266
|
chunk(rows, DOCUMENTS_PER_STATEMENT).map((slice) =>
|
|
262
267
|
this.search
|
|
263
268
|
.insert(threadMessageSearchTable)
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
type SqliteDatabases,
|
|
3
|
+
type SqliteStores,
|
|
4
|
+
syncStores,
|
|
5
|
+
} from "./databases.js";
|
|
2
6
|
import { CalendarCollectionRepo } from "./repos/calendar-collection.js";
|
|
3
7
|
import { CalendarEventIndexRepo } from "./repos/calendar-event-index.js";
|
|
4
8
|
import { CalendarFeedTokenRepo } from "./repos/calendar-feed-token.js";
|
|
@@ -31,45 +35,57 @@ import { SyncWarningRepo } from "./repos/sync-warning.js";
|
|
|
31
35
|
import { DrizzleThreadMessageRepository } from "./repos/thread-message.js";
|
|
32
36
|
import { SearchIndex } from "./search/thread-message-search.js";
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
export const createSqliteRepositories = ({
|
|
38
|
+
export const composeSqliteRepositories = ({
|
|
36
39
|
app,
|
|
37
40
|
mail,
|
|
38
41
|
search,
|
|
39
|
-
}:
|
|
40
|
-
const searchIndex = new SearchIndex(mail, search);
|
|
42
|
+
}: SqliteStores) => {
|
|
43
|
+
const searchIndex = new SearchIndex(mail.db, search);
|
|
44
|
+
const accountSetting = new AccountSettingRepo(app.db);
|
|
45
|
+
const address = new AddressRepo(mail.db);
|
|
46
|
+
const mailbox = new MailboxRepo(mail, searchIndex);
|
|
47
|
+
const account = new AccountRepo(app.db, mailbox);
|
|
48
|
+
const calendarSuggestion = new CalendarSuggestionRepo(mail.db);
|
|
41
49
|
return {
|
|
42
|
-
accountConfig: new AccountConfigRepo(app,
|
|
43
|
-
account
|
|
44
|
-
accountSetting
|
|
45
|
-
address
|
|
46
|
-
mailbox
|
|
47
|
-
mailboxSpecialUse: new MailboxSpecialUseRepo(
|
|
48
|
-
|
|
50
|
+
accountConfig: new AccountConfigRepo(app.db, address),
|
|
51
|
+
account,
|
|
52
|
+
accountSetting,
|
|
53
|
+
address,
|
|
54
|
+
mailbox,
|
|
55
|
+
mailboxSpecialUse: new MailboxSpecialUseRepo(
|
|
56
|
+
mail.db,
|
|
57
|
+
account,
|
|
58
|
+
accountSetting,
|
|
59
|
+
),
|
|
60
|
+
mailboxLock: new MailboxLockRepo(mail.db),
|
|
49
61
|
message: new DrizzleMessageRepository(mail, searchIndex),
|
|
50
|
-
messageFlag: new DrizzleMessageFlagRepository(mail),
|
|
51
|
-
outboxMessage: new OutboxMessageRepo(mail),
|
|
62
|
+
messageFlag: new DrizzleMessageFlagRepository(mail.db),
|
|
63
|
+
outboxMessage: new OutboxMessageRepo(mail.db),
|
|
52
64
|
outboxAttachment: new OutboxAttachmentRepo(mail),
|
|
53
65
|
threadMessage: new DrizzleThreadMessageRepository(mail, searchIndex),
|
|
54
66
|
envelope: new DrizzleEnvelopeRepository(mail),
|
|
55
|
-
accountExportRequest: new AccountExportRequestRepo(app),
|
|
56
|
-
configImport: new ConfigImportRepo(app),
|
|
57
|
-
quarantine: new QuarantineRepo(mail),
|
|
58
|
-
syncWarning: new SyncWarningRepo(mail),
|
|
59
|
-
organizeJobRequest: new OrganizeJobRequestRepo(mail),
|
|
60
|
-
placementMove: new MessagePlacementMoveRepo(mail),
|
|
61
|
-
flagPush: new MessageFlagPushRepo(mail),
|
|
62
|
-
filter: new FilterRepo(app),
|
|
63
|
-
label: new LabelRepo(app),
|
|
64
|
-
messageLabel: new MessageLabelRepo(mail),
|
|
65
|
-
senderSignerStanding: new SenderSignerStandingRepo(mail),
|
|
66
|
-
calendarCollection: new CalendarCollectionRepo(app),
|
|
67
|
-
calendarObject: new CalendarObjectRepo(app),
|
|
68
|
-
calendarEventIndex: new CalendarEventIndexRepo(app),
|
|
69
|
-
calendarFeedToken: new CalendarFeedTokenRepo(app),
|
|
70
|
-
calendarSuggestion
|
|
71
|
-
calendarStore: new DrizzleCalendarStore(app,
|
|
67
|
+
accountExportRequest: new AccountExportRequestRepo(app.db),
|
|
68
|
+
configImport: new ConfigImportRepo(app.db),
|
|
69
|
+
quarantine: new QuarantineRepo(mail.db),
|
|
70
|
+
syncWarning: new SyncWarningRepo(mail.db),
|
|
71
|
+
organizeJobRequest: new OrganizeJobRequestRepo(mail.db),
|
|
72
|
+
placementMove: new MessagePlacementMoveRepo(mail.db),
|
|
73
|
+
flagPush: new MessageFlagPushRepo(mail.db),
|
|
74
|
+
filter: new FilterRepo(app.db),
|
|
75
|
+
label: new LabelRepo(app.db),
|
|
76
|
+
messageLabel: new MessageLabelRepo(mail.db),
|
|
77
|
+
senderSignerStanding: new SenderSignerStandingRepo(mail.db),
|
|
78
|
+
calendarCollection: new CalendarCollectionRepo(app.db),
|
|
79
|
+
calendarObject: new CalendarObjectRepo(app.db),
|
|
80
|
+
calendarEventIndex: new CalendarEventIndexRepo(app.db),
|
|
81
|
+
calendarFeedToken: new CalendarFeedTokenRepo(app.db),
|
|
82
|
+
calendarSuggestion,
|
|
83
|
+
calendarStore: new DrizzleCalendarStore(app, calendarSuggestion),
|
|
72
84
|
};
|
|
73
85
|
};
|
|
74
86
|
|
|
75
|
-
export type SqliteRepositories = ReturnType<typeof
|
|
87
|
+
export type SqliteRepositories = ReturnType<typeof composeSqliteRepositories>;
|
|
88
|
+
|
|
89
|
+
export const createSqliteRepositories = (
|
|
90
|
+
databases: SqliteDatabases,
|
|
91
|
+
): SqliteRepositories => composeSqliteRepositories(syncStores(databases));
|