@remit/drizzle-service 0.0.40 → 0.0.42
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 +2 -2
- package/src/index.ts +1 -0
- package/src/repos/i4-address.test.ts +39 -0
- package/src/repos/i4-address.ts +2 -1
- package/src/repos/i4-sender-signer-standing.test.ts +188 -0
- package/src/repos/i4-sender-signer-standing.ts +101 -0
- package/src/repos/message.ts +6 -0
- package/src/repos/sender-signer-standing.conformance.sqlite.test.ts +23 -0
- package/src/schema.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/drizzle-service",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.42",
|
|
4
4
|
"description": "Drizzle ORM service over SQLite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"test:typecheck": "tsgo --noEmit -p tsconfig.json",
|
|
16
|
-
"test:run": "node --env-file=../../localhost-test-unit-sqlite.env --import tsx --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-exclude='src/**/*.test.ts' --test-coverage-exclude='src/**/*.test.tsx' --test-coverage-lines=90 --test 'src/**/*.test.ts'",
|
|
16
|
+
"test:run": "node $NODE_TEST_FLAGS --env-file=../../localhost-test-unit-sqlite.env --import tsx --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-exclude='src/**/*.test.ts' --test-coverage-exclude='src/**/*.test.tsx' --test-coverage-lines=90 --test 'src/**/*.test.ts'",
|
|
17
17
|
"test": "npm run test:typecheck && npm run test:run",
|
|
18
18
|
"fix": "biome check --write src"
|
|
19
19
|
},
|
package/src/index.ts
CHANGED
|
@@ -33,6 +33,7 @@ export {
|
|
|
33
33
|
export * from "./repos/i4-organize-job-request.js";
|
|
34
34
|
export { OutboxAttachmentRepo } from "./repos/i4-outbox-attachment.js";
|
|
35
35
|
export * from "./repos/i4-outbox-message.js";
|
|
36
|
+
export { SenderSignerStandingRepo } from "./repos/i4-sender-signer-standing.js";
|
|
36
37
|
export { LabelRepo } from "./repos/label.js";
|
|
37
38
|
export {
|
|
38
39
|
DrizzleMessageRepository,
|
|
@@ -98,6 +98,45 @@ describe("AddressRepo", () => {
|
|
|
98
98
|
await repo.deleteAddress(addr.accountConfigId, addr.addressId);
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
+
test("three inbound messages promote a person-shaped sender to wellknown", async () => {
|
|
102
|
+
const addr = await repo.createAddress(makeAddressInput(randomId()));
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
|
|
105
|
+
for (let i = 0; i < 3; i++) {
|
|
106
|
+
await repo.incrementInboundCount(
|
|
107
|
+
addr.accountConfigId,
|
|
108
|
+
addr.addressId,
|
|
109
|
+
now,
|
|
110
|
+
false,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const updated = await repo.getAddress(addr.accountConfigId, addr.addressId);
|
|
115
|
+
assert.equal(updated.flags?.wellknown?.value, true);
|
|
116
|
+
|
|
117
|
+
await repo.deleteAddress(addr.accountConfigId, addr.addressId);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("a bulk sender never reaches wellknown on inbound volume alone", async () => {
|
|
121
|
+
const addr = await repo.createAddress(makeAddressInput(randomId()));
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < 5; i++) {
|
|
125
|
+
await repo.incrementInboundCount(
|
|
126
|
+
addr.accountConfigId,
|
|
127
|
+
addr.addressId,
|
|
128
|
+
now,
|
|
129
|
+
true,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const updated = await repo.getAddress(addr.accountConfigId, addr.addressId);
|
|
134
|
+
assert.equal(updated.inboundCount, 5);
|
|
135
|
+
assert.equal(updated.flags?.wellknown, undefined);
|
|
136
|
+
|
|
137
|
+
await repo.deleteAddress(addr.accountConfigId, addr.addressId);
|
|
138
|
+
});
|
|
139
|
+
|
|
101
140
|
test("createEnvelopeAddress and getEnvelopeAddress", async () => {
|
|
102
141
|
const messageId = randomUUID();
|
|
103
142
|
const addressId = randomUUID();
|
package/src/repos/i4-address.ts
CHANGED
|
@@ -396,13 +396,14 @@ export class AddressRepo implements IAddressRepository {
|
|
|
396
396
|
accountConfigId: string,
|
|
397
397
|
addressId: string,
|
|
398
398
|
now: number,
|
|
399
|
-
|
|
399
|
+
isBulk?: boolean,
|
|
400
400
|
): Promise<void> {
|
|
401
401
|
const current = await this.getAddress(accountConfigId, addressId);
|
|
402
402
|
const post = {
|
|
403
403
|
...current,
|
|
404
404
|
inboundCount: (current.inboundCount ?? 0) + 1,
|
|
405
405
|
lastInboundAt: now,
|
|
406
|
+
isBulk: isBulk ?? false,
|
|
406
407
|
};
|
|
407
408
|
if (shouldPromoteWellknown(post, now)) {
|
|
408
409
|
const nextFlags: AddressFlags = {
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { after, before, describe, test } from "node:test";
|
|
3
|
+
import { eq } from "drizzle-orm";
|
|
4
|
+
import { NotFoundError } from "../error.js";
|
|
5
|
+
import { senderSignerStandingTable } from "../schema.js";
|
|
6
|
+
import { createTestDb, randomId, type TestDb } from "../test-db.js";
|
|
7
|
+
import { SenderSignerStandingRepo } from "./i4-sender-signer-standing.js";
|
|
8
|
+
|
|
9
|
+
describe("SenderSignerStandingRepo", () => {
|
|
10
|
+
let db: TestDb;
|
|
11
|
+
let close: () => Promise<void>;
|
|
12
|
+
let repo: SenderSignerStandingRepo;
|
|
13
|
+
|
|
14
|
+
const rowsFor = async (accountConfigId: string) =>
|
|
15
|
+
db
|
|
16
|
+
.select()
|
|
17
|
+
.from(senderSignerStandingTable)
|
|
18
|
+
.where(eq(senderSignerStandingTable.accountConfigId, accountConfigId));
|
|
19
|
+
|
|
20
|
+
before(async () => {
|
|
21
|
+
({ db, close } = await createTestDb());
|
|
22
|
+
repo = new SenderSignerStandingRepo(db as never);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
after(async () => {
|
|
26
|
+
await close();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("observing a new key inserts one row at a count of one", async () => {
|
|
30
|
+
const accountConfigId = randomId();
|
|
31
|
+
const observedAt = 1_700_000_000_000;
|
|
32
|
+
|
|
33
|
+
const standing = await repo.observe({
|
|
34
|
+
accountConfigId,
|
|
35
|
+
senderKey: "vip.example",
|
|
36
|
+
signerDomain: "esp.example",
|
|
37
|
+
observedAt,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
assert.equal(standing.messageCount, 1);
|
|
41
|
+
assert.equal(standing.firstSeenAt, observedAt);
|
|
42
|
+
assert.equal(standing.lastSeenAt, observedAt);
|
|
43
|
+
assert.equal(standing.userAffirmedAt, 0);
|
|
44
|
+
assert.equal((await rowsFor(accountConfigId)).length, 1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("observing the same key again increments the count in place rather than inserting a second row", async () => {
|
|
48
|
+
const accountConfigId = randomId();
|
|
49
|
+
const key = {
|
|
50
|
+
accountConfigId,
|
|
51
|
+
senderKey: "vip.example",
|
|
52
|
+
signerDomain: "esp.example",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
await repo.observe({ ...key, observedAt: 1_700_000_000_000 });
|
|
56
|
+
await repo.observe({ ...key, observedAt: 1_700_000_060_000 });
|
|
57
|
+
const third = await repo.observe({
|
|
58
|
+
...key,
|
|
59
|
+
observedAt: 1_700_000_120_000,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
assert.equal(third.messageCount, 3);
|
|
63
|
+
const rows = await rowsFor(accountConfigId);
|
|
64
|
+
assert.equal(rows.length, 1);
|
|
65
|
+
assert.equal(rows[0]?.messageCount, 3);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// The failure this guards is silent: naming first_seen_at in the conflict
|
|
69
|
+
// `set` makes every message reset the key's age, so standing reads as
|
|
70
|
+
// brand-new forever and nothing downstream can tell.
|
|
71
|
+
test("a repeat leaves firstSeenAt at the first observation while lastSeenAt follows the latest", async () => {
|
|
72
|
+
const accountConfigId = randomId();
|
|
73
|
+
const key = {
|
|
74
|
+
accountConfigId,
|
|
75
|
+
senderKey: "list.example",
|
|
76
|
+
signerDomain: "unverified",
|
|
77
|
+
};
|
|
78
|
+
const first = 1_600_000_000_000;
|
|
79
|
+
|
|
80
|
+
await repo.observe({ ...key, observedAt: first });
|
|
81
|
+
await repo.observe({ ...key, observedAt: first + 3_600_000 });
|
|
82
|
+
const latest = await repo.observe({
|
|
83
|
+
...key,
|
|
84
|
+
observedAt: first + 7_200_000,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
assert.equal(latest.firstSeenAt, first);
|
|
88
|
+
assert.equal(latest.lastSeenAt, first + 7_200_000);
|
|
89
|
+
|
|
90
|
+
const [row] = await rowsFor(accountConfigId);
|
|
91
|
+
assert.equal(row?.firstSeenAt, first);
|
|
92
|
+
assert.equal(row?.lastSeenAt, first + 7_200_000);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("an out-of-order observation still counts, and never rewrites firstSeenAt", async () => {
|
|
96
|
+
const accountConfigId = randomId();
|
|
97
|
+
const key = {
|
|
98
|
+
accountConfigId,
|
|
99
|
+
senderKey: "delayed.example",
|
|
100
|
+
signerDomain: "esp.example",
|
|
101
|
+
};
|
|
102
|
+
const first = 1_650_000_000_000;
|
|
103
|
+
|
|
104
|
+
await repo.observe({ ...key, observedAt: first });
|
|
105
|
+
const older = await repo.observe({
|
|
106
|
+
...key,
|
|
107
|
+
observedAt: first - 86_400_000,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
assert.equal(older.messageCount, 2);
|
|
111
|
+
assert.equal(older.firstSeenAt, first);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("the same sender under two signer domains keeps two independent rows", async () => {
|
|
115
|
+
const accountConfigId = randomId();
|
|
116
|
+
const observedAt = 1_700_000_000_000;
|
|
117
|
+
|
|
118
|
+
await repo.observe({
|
|
119
|
+
accountConfigId,
|
|
120
|
+
senderKey: "shop.example",
|
|
121
|
+
signerDomain: "esp-one.example",
|
|
122
|
+
observedAt,
|
|
123
|
+
});
|
|
124
|
+
await repo.observe({
|
|
125
|
+
accountConfigId,
|
|
126
|
+
senderKey: "shop.example",
|
|
127
|
+
signerDomain: "esp-one.example",
|
|
128
|
+
observedAt,
|
|
129
|
+
});
|
|
130
|
+
const other = await repo.observe({
|
|
131
|
+
accountConfigId,
|
|
132
|
+
senderKey: "shop.example",
|
|
133
|
+
signerDomain: "esp-two.example",
|
|
134
|
+
observedAt,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
assert.equal(other.messageCount, 1);
|
|
138
|
+
assert.equal((await rowsFor(accountConfigId)).length, 2);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("standing never crosses accounts", async () => {
|
|
142
|
+
const mine = randomId();
|
|
143
|
+
const theirs = randomId();
|
|
144
|
+
const key = {
|
|
145
|
+
senderKey: "shared.example",
|
|
146
|
+
signerDomain: "esp.example",
|
|
147
|
+
observedAt: 1_700_000_000_000,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
await repo.observe({ accountConfigId: mine, ...key });
|
|
151
|
+
await repo.observe({ accountConfigId: mine, ...key });
|
|
152
|
+
const foreign = await repo.observe({ accountConfigId: theirs, ...key });
|
|
153
|
+
|
|
154
|
+
assert.equal(foreign.messageCount, 1);
|
|
155
|
+
assert.equal(
|
|
156
|
+
(await repo.get(mine, key.senderKey, key.signerDomain)).messageCount,
|
|
157
|
+
2,
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("get raises NotFoundError for a key that was never observed", async () => {
|
|
162
|
+
await assert.rejects(
|
|
163
|
+
repo.get(randomId(), "stranger.example", "esp.example"),
|
|
164
|
+
(error) => error instanceof NotFoundError,
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("get reads back the row the last observation returned", async () => {
|
|
169
|
+
const accountConfigId = randomId();
|
|
170
|
+
const key = {
|
|
171
|
+
accountConfigId,
|
|
172
|
+
senderKey: "readback.example",
|
|
173
|
+
signerDomain: "esp.example",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const written = await repo.observe({
|
|
177
|
+
...key,
|
|
178
|
+
observedAt: 1_700_000_000_000,
|
|
179
|
+
});
|
|
180
|
+
const read = await repo.get(
|
|
181
|
+
accountConfigId,
|
|
182
|
+
key.senderKey,
|
|
183
|
+
key.signerDomain,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
assert.deepEqual(read, written);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ISenderSignerStandingRepository,
|
|
3
|
+
ObserveSenderSignerStandingInput,
|
|
4
|
+
SenderSignerStandingItem,
|
|
5
|
+
} from "@remit/data-ports";
|
|
6
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
7
|
+
import type { Db } from "../db.js";
|
|
8
|
+
import { NotFoundError } from "../error.js";
|
|
9
|
+
import { senderSignerStandingTable } from "../schema.js";
|
|
10
|
+
|
|
11
|
+
type DB = Db<Record<string, unknown>>;
|
|
12
|
+
|
|
13
|
+
function rowToStanding(
|
|
14
|
+
row: typeof senderSignerStandingTable.$inferSelect,
|
|
15
|
+
): SenderSignerStandingItem {
|
|
16
|
+
return {
|
|
17
|
+
accountConfigId: row.accountConfigId,
|
|
18
|
+
senderKey: row.senderKey,
|
|
19
|
+
signerDomain: row.signerDomain,
|
|
20
|
+
messageCount: row.messageCount,
|
|
21
|
+
firstSeenAt: row.firstSeenAt,
|
|
22
|
+
lastSeenAt: row.lastSeenAt,
|
|
23
|
+
userAffirmedAt: row.userAffirmedAt,
|
|
24
|
+
createdAt: row.createdAt,
|
|
25
|
+
updatedAt: row.updatedAt,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class SenderSignerStandingRepo
|
|
30
|
+
implements ISenderSignerStandingRepository
|
|
31
|
+
{
|
|
32
|
+
constructor(private db: DB) {}
|
|
33
|
+
|
|
34
|
+
async observe(
|
|
35
|
+
input: ObserveSenderSignerStandingInput,
|
|
36
|
+
): Promise<SenderSignerStandingItem> {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
// `first_seen_at` is deliberately absent from the conflict `set`.
|
|
39
|
+
// onConflictDoUpdate writes only the columns it names, so naming it here
|
|
40
|
+
// would let every message reset the key's age to its own arrival and the
|
|
41
|
+
// standing this row exists to record would never be older than the last
|
|
42
|
+
// message. The mirror-image mistake — a conflict path that must reset a
|
|
43
|
+
// timestamp and would silently inherit the old one by omitting it — is at
|
|
44
|
+
// i4-message-flag-push.ts:70.
|
|
45
|
+
const [row] = await this.db
|
|
46
|
+
.insert(senderSignerStandingTable)
|
|
47
|
+
.values({
|
|
48
|
+
accountConfigId: input.accountConfigId,
|
|
49
|
+
senderKey: input.senderKey,
|
|
50
|
+
signerDomain: input.signerDomain,
|
|
51
|
+
messageCount: 1,
|
|
52
|
+
firstSeenAt: input.observedAt,
|
|
53
|
+
lastSeenAt: input.observedAt,
|
|
54
|
+
userAffirmedAt: 0,
|
|
55
|
+
createdAt: now,
|
|
56
|
+
updatedAt: now,
|
|
57
|
+
})
|
|
58
|
+
.onConflictDoUpdate({
|
|
59
|
+
target: [
|
|
60
|
+
senderSignerStandingTable.accountConfigId,
|
|
61
|
+
senderSignerStandingTable.senderKey,
|
|
62
|
+
senderSignerStandingTable.signerDomain,
|
|
63
|
+
],
|
|
64
|
+
set: {
|
|
65
|
+
messageCount: sql`${senderSignerStandingTable.messageCount} + 1`,
|
|
66
|
+
lastSeenAt: input.observedAt,
|
|
67
|
+
updatedAt: now,
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
.returning();
|
|
71
|
+
if (!row) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Failed to upsert SenderSignerStanding: ${input.accountConfigId}/${input.senderKey}/${input.signerDomain}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return rowToStanding(row);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async get(
|
|
80
|
+
accountConfigId: string,
|
|
81
|
+
senderKey: string,
|
|
82
|
+
signerDomain: string,
|
|
83
|
+
): Promise<SenderSignerStandingItem> {
|
|
84
|
+
const [row] = await this.db
|
|
85
|
+
.select()
|
|
86
|
+
.from(senderSignerStandingTable)
|
|
87
|
+
.where(
|
|
88
|
+
and(
|
|
89
|
+
eq(senderSignerStandingTable.accountConfigId, accountConfigId),
|
|
90
|
+
eq(senderSignerStandingTable.senderKey, senderKey),
|
|
91
|
+
eq(senderSignerStandingTable.signerDomain, signerDomain),
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
if (!row) {
|
|
95
|
+
throw new NotFoundError(
|
|
96
|
+
`SenderSignerStanding not found: ${senderKey}/${signerDomain}`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return rowToStanding(row);
|
|
100
|
+
}
|
|
101
|
+
}
|
package/src/repos/message.ts
CHANGED
|
@@ -59,6 +59,7 @@ function toMessageItem(row: typeof messageTable.$inferSelect): MessageItem {
|
|
|
59
59
|
status: row.status,
|
|
60
60
|
syncStatus: row.syncStatus,
|
|
61
61
|
category: row.category,
|
|
62
|
+
authenticityVerdict: row.authenticityVerdict,
|
|
62
63
|
hasListUnsubscribe: row.hasListUnsubscribe,
|
|
63
64
|
movedByRemit: row.movedByRemit,
|
|
64
65
|
createdAt: row.createdAt,
|
|
@@ -184,6 +185,8 @@ export class DrizzleMessageRepository implements IMessageRepository {
|
|
|
184
185
|
status: input.status ?? ("active" as const),
|
|
185
186
|
syncStatus: input.syncStatus ?? ("pending" as const),
|
|
186
187
|
category: input.category ?? ("uncategorized" as const),
|
|
188
|
+
authenticityVerdict:
|
|
189
|
+
input.authenticityVerdict ?? ("NotEvaluated" as const),
|
|
187
190
|
hasListUnsubscribe: input.hasListUnsubscribe ?? false,
|
|
188
191
|
movedByRemit: input.movedByRemit ?? false,
|
|
189
192
|
messageIdHeader: input.messageIdHeader ?? null,
|
|
@@ -365,6 +368,9 @@ export class DrizzleMessageRepository implements IMessageRepository {
|
|
|
365
368
|
? { syncStatus: input.syncStatus }
|
|
366
369
|
: {}),
|
|
367
370
|
...(input.category !== undefined ? { category: input.category } : {}),
|
|
371
|
+
...(input.authenticityVerdict !== undefined
|
|
372
|
+
? { authenticityVerdict: input.authenticityVerdict }
|
|
373
|
+
: {}),
|
|
368
374
|
...(input.hasListUnsubscribe !== undefined
|
|
369
375
|
? { hasListUnsubscribe: input.hasListUnsubscribe }
|
|
370
376
|
: {}),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { senderSignerStandingRepositoryConformance } from "@remit/data-ports/conformance";
|
|
2
|
+
import { NotFoundError } from "../error.js";
|
|
3
|
+
import { randomId } from "../id.js";
|
|
4
|
+
import { senderSignerStandingTable } from "../schema.js";
|
|
5
|
+
import { createSqliteTestDb } from "../test-db-sqlite.js";
|
|
6
|
+
import { SenderSignerStandingRepo } from "./i4-sender-signer-standing.js";
|
|
7
|
+
|
|
8
|
+
let close: (() => Promise<void>) | undefined;
|
|
9
|
+
|
|
10
|
+
senderSignerStandingRepositoryConformance({
|
|
11
|
+
async createRepository() {
|
|
12
|
+
const { db, close: closeDb } = await createSqliteTestDb({
|
|
13
|
+
senderSignerStandings: senderSignerStandingTable,
|
|
14
|
+
});
|
|
15
|
+
close = closeDb;
|
|
16
|
+
return new SenderSignerStandingRepo(db as never);
|
|
17
|
+
},
|
|
18
|
+
teardown: async () => {
|
|
19
|
+
await close?.();
|
|
20
|
+
},
|
|
21
|
+
makeId: () => randomId(),
|
|
22
|
+
isNotFoundError: (error) => error instanceof NotFoundError,
|
|
23
|
+
});
|
package/src/schema.ts
CHANGED
|
@@ -14,6 +14,7 @@ export const labelTable = entities.labels;
|
|
|
14
14
|
export const mailboxAttributeEntryTable = entities.mailboxAttributeEntries;
|
|
15
15
|
export const mailboxFlagTable = entities.mailboxFlags;
|
|
16
16
|
export const messageLabelTable = entities.messageLabels;
|
|
17
|
+
export const senderSignerStandingTable = entities.senderSignerStandings;
|
|
17
18
|
export * from "./schema/i4-account-config.js";
|
|
18
19
|
export * from "./schema/i4-account-export-request.js";
|
|
19
20
|
export * from "./schema/i4-account-setting.js";
|