@spinajs/email-verification-dynamodb 0.0.0-stage → 3.0.9

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 CHANGED
@@ -1,3 +1,9 @@
1
- # Temporary Holding Version
1
+ # `@spinajs/email-verification-dynamodb`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DynamoDB store for `@spinajs/email-verification` (AWS SDK v3). Select it with
4
+ `emailVerification.store: 'DynamoDbEmailVerificationStore'`. It creates the table `emailVerification.dynamodb.table`
5
+ (hash key `pk`, on-demand billing, TTL on `ExpiresAt`) unless `createTable` is false.
6
+
7
+ ## Documentation
8
+
9
+ - [Package overview](../../docs/email-verification-dynamodb/README.md)
@@ -0,0 +1,70 @@
1
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import type { AttributeValue, DynamoDBClientConfig } from '@aws-sdk/client-dynamodb';
3
+ import { EmailVerificationStore } from '@spinajs/email-verification';
4
+ import type { CachedVerdict, DynamoDbStoreOptions, ListEntry, ListKind } from '@spinajs/email-verification';
5
+ import { Log } from '@spinajs/log';
6
+ type Item = Record<string, AttributeValue>;
7
+ interface Condition {
8
+ ConditionExpression: string;
9
+ ExpressionAttributeNames: Record<string, string>;
10
+ ExpressionAttributeValues?: Item;
11
+ }
12
+ interface Generation {
13
+ gen: string;
14
+ chunks: number;
15
+ }
16
+ export declare const CHUNK_BYTES = 300000;
17
+ export declare const INDEX_PK = "S#index";
18
+ export declare function chunkByBytes(domains: readonly string[], maxBytes: number): string[][];
19
+ /**
20
+ * DynamoDB store (AWS SDK v3), one table with the string hash key `pk`. It uses no Scan.
21
+ * Each list source is a manifest (`S#<kind>#<source>`: live `Gen` and `Chunks` count, the
22
+ * replaced `PrevGen` and `PrevChunks`) over chunk items of newline-joined domains, plus an
23
+ * index item of the sources. A write puts a new generation of chunks, compare-and-sets the
24
+ * manifest, then deletes the replaced generation and the one before it, so the next write
25
+ * reclaims what a writer that died after its swap left behind. An emptied source keeps a
26
+ * manifest of zero chunks and its index member: removing either could race a writer
27
+ * re-creating the source. A reader that finds a chunk missing has raced a write, and reads
28
+ * the manifest again.
29
+ */
30
+ export declare class DynamoDbEmailVerificationStore extends EmailVerificationStore {
31
+ protected Options: DynamoDbStoreOptions;
32
+ protected Log: Log;
33
+ protected Client: DynamoDBClient;
34
+ resolve(): Promise<void>;
35
+ dispose(): Promise<void>;
36
+ getVerdict(verifier: string, domain: string): Promise<CachedVerdict | null>;
37
+ setVerdict(verifier: string, domain: string, verdict: CachedVerdict, ttlSeconds: number): Promise<void>;
38
+ getMeta(key: string): Promise<string | null>;
39
+ setMeta(key: string, value: string): Promise<void>;
40
+ listVersion(): Promise<string>;
41
+ loadLists(): Promise<ListEntry[]>;
42
+ upsertEntries(entries: readonly ListEntry[]): Promise<void>;
43
+ removeEntries(kind: ListKind, domains: readonly string[], source: string): Promise<void>;
44
+ replaceSource(source: string, kind: ListKind, domains: readonly string[]): Promise<void>;
45
+ protected createClient(cfg: DynamoDBClientConfig): DynamoDBClient;
46
+ protected mutateSource(kind: ListKind, source: string, change: (current: Set<string>) => Set<string>, readCurrent?: boolean): Promise<void>;
47
+ protected putChunks(kind: ListKind, source: string, gen: string, chunks: string[][]): Promise<void>;
48
+ /**
49
+ * true when the manifest now names `gen` (or named it before a later writer replaced it).
50
+ * A failure with an unknown outcome is rethrown and keeps the chunks, as the put may still land.
51
+ */
52
+ protected swapManifest(kind: ListKind, source: string, old: Item | undefined, gen: string, chunks: number): Promise<boolean>;
53
+ /** Runs every step even when one fails, then rethrows the first failure. */
54
+ protected settleSwap(kind: ListKind, source: string, replaced: Item | undefined, live: boolean): Promise<void>;
55
+ /** null when the source stayed unreadable through every retry. */
56
+ protected readSource(kind: ListKind, source: string): Promise<string[] | null>;
57
+ /** null when a chunk is gone because a writer replaced this generation during the read; throws when the manifest is unchanged, i.e. the data is damaged. */
58
+ protected readChunks(kind: ListKind, source: string, manifest: Item): Promise<string[] | null>;
59
+ /** Best effort: a generation left behind here is reclaimed by a later write to the source. */
60
+ protected dropGenerations(kind: ListKind, source: string, generations: Generation[]): Promise<void>;
61
+ protected indexMembers(): Promise<string[]>;
62
+ protected addToIndex(member: string): Promise<void>;
63
+ protected get(pk: string, consistent?: boolean): Promise<Item | undefined>;
64
+ protected put(item: Item, condition?: Condition): Promise<void>;
65
+ protected ensureTable(): Promise<void>;
66
+ protected createTable(): Promise<void>;
67
+ protected enableTtl(): Promise<void>;
68
+ }
69
+ export {};
70
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.DynamoDbEmailVerificationStore = exports.INDEX_PK = exports.CHUNK_BYTES = void 0;
13
+ exports.chunkByBytes = chunkByBytes;
14
+ const node_crypto_1 = require("node:crypto");
15
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
16
+ const configuration_1 = require("@spinajs/configuration");
17
+ const di_1 = require("@spinajs/di");
18
+ const email_verification_1 = require("@spinajs/email-verification");
19
+ const exceptions_1 = require("@spinajs/exceptions");
20
+ const log_1 = require("@spinajs/log");
21
+ class DamagedListError extends exceptions_1.InvalidOperation {
22
+ }
23
+ // DynamoDB caps an item at 400 KB; the rest is headroom for the key and attribute names.
24
+ exports.CHUNK_BYTES = 300_000;
25
+ exports.INDEX_PK = 'S#index';
26
+ const VERSION_PK = 'L#version';
27
+ const TTL_ATTRIBUTE = 'ExpiresAt';
28
+ const MAX_ATTEMPTS = 5;
29
+ const BACKOFF_MS = 20;
30
+ const s = (value) => ({ S: value });
31
+ const n = (value) => ({ N: String(value) });
32
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
33
+ const errorName = (err) => err?.name;
34
+ const isConditionFailure = (err) => errorName(err) === 'ConditionalCheckFailedException';
35
+ const sourcePk = (kind, source) => `S#${kind}#${source}`;
36
+ const chunkPk = (kind, source, gen, i) => `C#${kind}#${source}#${gen}#${i}`;
37
+ const notExists = { ConditionExpression: 'attribute_not_exists(#pk)', ExpressionAttributeNames: { '#pk': 'pk' } };
38
+ const equals = (name, value) => ({ ConditionExpression: '#a = :v', ExpressionAttributeNames: { '#a': name }, ExpressionAttributeValues: { ':v': value } });
39
+ /** The live generation of a manifest and the one it replaced. */
40
+ const generationsOf = (manifest) => [{ gen: manifest.Gen.S, chunks: Number(manifest.Chunks.N) }, ...(manifest.PrevGen ? [{ gen: manifest.PrevGen.S, chunks: Number(manifest.PrevChunks.N) }] : [])];
41
+ function chunkByBytes(domains, maxBytes) {
42
+ const chunks = [];
43
+ let current = [];
44
+ let size = 0;
45
+ for (const d of domains) {
46
+ const bytes = Buffer.byteLength(d) + 1;
47
+ if (current.length > 0 && size + bytes > maxBytes) {
48
+ chunks.push(current);
49
+ current = [];
50
+ size = 0;
51
+ }
52
+ current.push(d);
53
+ size += bytes;
54
+ }
55
+ if (current.length > 0)
56
+ chunks.push(current);
57
+ return chunks;
58
+ }
59
+ /**
60
+ * DynamoDB store (AWS SDK v3), one table with the string hash key `pk`. It uses no Scan.
61
+ * Each list source is a manifest (`S#<kind>#<source>`: live `Gen` and `Chunks` count, the
62
+ * replaced `PrevGen` and `PrevChunks`) over chunk items of newline-joined domains, plus an
63
+ * index item of the sources. A write puts a new generation of chunks, compare-and-sets the
64
+ * manifest, then deletes the replaced generation and the one before it, so the next write
65
+ * reclaims what a writer that died after its swap left behind. An emptied source keeps a
66
+ * manifest of zero chunks and its index member: removing either could race a writer
67
+ * re-creating the source. A reader that finds a chunk missing has raced a write, and reads
68
+ * the manifest again.
69
+ */
70
+ let DynamoDbEmailVerificationStore = class DynamoDbEmailVerificationStore extends email_verification_1.EmailVerificationStore {
71
+ async resolve() {
72
+ this.Client = this.createClient(this.Options.config);
73
+ if (this.Options.createTable)
74
+ await this.ensureTable();
75
+ await super.resolve();
76
+ }
77
+ async dispose() {
78
+ this.Client?.destroy();
79
+ }
80
+ async getVerdict(verifier, domain) {
81
+ const item = await this.get(`V#${verifier}#${domain}`, false);
82
+ if (!item || Number(item.ExpiresAt.N) * 1000 <= Date.now())
83
+ return null;
84
+ return JSON.parse(item.Verdict.S);
85
+ }
86
+ async setVerdict(verifier, domain, verdict, ttlSeconds) {
87
+ if (ttlSeconds <= 0)
88
+ return;
89
+ await this.put({ pk: s(`V#${verifier}#${domain}`), Verdict: s(JSON.stringify(verdict)), ExpiresAt: n(Math.floor((Date.now() + ttlSeconds * 1000) / 1000)) });
90
+ }
91
+ async getMeta(key) {
92
+ return (await this.get(`M#${key}`))?.Value?.S ?? null;
93
+ }
94
+ async setMeta(key, value) {
95
+ await this.put({ pk: s(`M#${key}`), Value: s(value) });
96
+ }
97
+ async listVersion() {
98
+ return (await this.get(VERSION_PK))?.Value?.S ?? '0';
99
+ }
100
+ async loadLists() {
101
+ const entries = [];
102
+ for (const m of await this.indexMembers()) {
103
+ const at = m.indexOf('#');
104
+ const kind = m.slice(0, at);
105
+ const source = m.slice(at + 1);
106
+ let domains;
107
+ try {
108
+ domains = await this.readSource(kind, source);
109
+ }
110
+ catch (err) {
111
+ if (!(err instanceof DamagedListError))
112
+ throw err;
113
+ this.Log.warn(`${err.message}; skipped`);
114
+ continue;
115
+ }
116
+ if (domains === null) {
117
+ this.Log.warn(`Email domain list ${kind}#${source} stayed unreadable through ${MAX_ATTEMPTS} reads; skipped`);
118
+ continue;
119
+ }
120
+ for (const domain of domains)
121
+ entries.push({ domain, kind, source });
122
+ }
123
+ return entries;
124
+ }
125
+ async upsertEntries(entries) {
126
+ const groups = new Map();
127
+ for (const e of entries) {
128
+ const key = `${e.kind}#${e.source}`;
129
+ const group = groups.get(key) ?? { kind: e.kind, source: e.source, domains: [] };
130
+ group.domains.push(e.domain);
131
+ groups.set(key, group);
132
+ }
133
+ for (const g of groups.values())
134
+ await this.mutateSource(g.kind, g.source, (current) => new Set([...current, ...g.domains]));
135
+ }
136
+ async removeEntries(kind, domains, source) {
137
+ await this.mutateSource(kind, source, (current) => {
138
+ domains.forEach((d) => current.delete(d));
139
+ return current;
140
+ });
141
+ }
142
+ async replaceSource(source, kind, domains) {
143
+ await this.mutateSource(kind, source, () => new Set(domains), false);
144
+ }
145
+ createClient(cfg) {
146
+ return new client_dynamodb_1.DynamoDBClient(cfg);
147
+ }
148
+ async mutateSource(kind, source, change, readCurrent = true) {
149
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
150
+ if (attempt > 0)
151
+ await sleep(BACKOFF_MS * attempt);
152
+ const manifest = await this.get(sourcePk(kind, source));
153
+ let current = [];
154
+ if (manifest && readCurrent) {
155
+ const read = await this.readChunks(kind, source, manifest);
156
+ if (read === null)
157
+ continue;
158
+ current = read;
159
+ }
160
+ const chunks = chunkByBytes([...change(new Set(current))], exports.CHUNK_BYTES);
161
+ const gen = (0, node_crypto_1.randomUUID)();
162
+ await this.putChunks(kind, source, gen, chunks);
163
+ if (!(await this.swapManifest(kind, source, manifest, gen, chunks.length))) {
164
+ await this.dropGenerations(kind, source, [{ gen, chunks: chunks.length }]);
165
+ continue;
166
+ }
167
+ await this.settleSwap(kind, source, manifest, chunks.length > 0);
168
+ return;
169
+ }
170
+ throw new exceptions_1.InvalidOperation(`email domain list ${kind}#${source} kept changing during ${MAX_ATTEMPTS} writes`);
171
+ }
172
+ // A crash during these puts leaks the generation: no manifest names it yet.
173
+ async putChunks(kind, source, gen, chunks) {
174
+ for (const [i, part] of chunks.entries()) {
175
+ try {
176
+ await this.put({ pk: s(chunkPk(kind, source, gen, i)), Domains: s(part.join('\n')) });
177
+ }
178
+ catch (err) {
179
+ await this.dropGenerations(kind, source, [{ gen, chunks: i + 1 }]);
180
+ throw err;
181
+ }
182
+ }
183
+ }
184
+ /**
185
+ * true when the manifest now names `gen` (or named it before a later writer replaced it).
186
+ * A failure with an unknown outcome is rethrown and keeps the chunks, as the put may still land.
187
+ */
188
+ async swapManifest(kind, source, old, gen, chunks) {
189
+ if (!old && chunks === 0)
190
+ return true;
191
+ const item = { pk: s(sourcePk(kind, source)), Gen: s(gen), Chunks: n(chunks), ...(old ? { PrevGen: old.Gen, PrevChunks: old.Chunks } : {}) };
192
+ try {
193
+ await this.put(item, old ? equals('Gen', old.Gen) : notExists);
194
+ return true;
195
+ }
196
+ catch (err) {
197
+ // The put can commit while the client sees an error, e.g. a retry failing on the put's own effect.
198
+ let now;
199
+ try {
200
+ now = await this.get(sourcePk(kind, source));
201
+ }
202
+ catch {
203
+ throw err;
204
+ }
205
+ // Two or more writers can swap between our committed put and this read: our change is then replayed on top of theirs.
206
+ if (now?.Gen.S === gen || now?.PrevGen?.S === gen)
207
+ return true;
208
+ if (isConditionFailure(err))
209
+ return false;
210
+ throw err;
211
+ }
212
+ }
213
+ /** Runs every step even when one fails, then rethrows the first failure. */
214
+ async settleSwap(kind, source, replaced, live) {
215
+ const failures = [];
216
+ // The index goes first: an instance that sees the new version must find the source in it.
217
+ if (live)
218
+ await this.addToIndex(`${kind}#${source}`).catch((err) => failures.push(err));
219
+ await this.put({ pk: s(VERSION_PK), Value: s((0, node_crypto_1.randomUUID)()) }).catch((err) => failures.push(err));
220
+ if (replaced)
221
+ await this.dropGenerations(kind, source, generationsOf(replaced));
222
+ if (failures.length > 0)
223
+ throw failures[0];
224
+ }
225
+ /** null when the source stayed unreadable through every retry. */
226
+ async readSource(kind, source) {
227
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
228
+ if (attempt > 0)
229
+ await sleep(BACKOFF_MS * attempt);
230
+ const manifest = await this.get(sourcePk(kind, source));
231
+ if (!manifest)
232
+ return [];
233
+ const domains = await this.readChunks(kind, source, manifest);
234
+ if (domains !== null)
235
+ return domains;
236
+ }
237
+ return null;
238
+ }
239
+ /** null when a chunk is gone because a writer replaced this generation during the read; throws when the manifest is unchanged, i.e. the data is damaged. */
240
+ async readChunks(kind, source, manifest) {
241
+ const domains = [];
242
+ for (let i = 0; i < Number(manifest.Chunks.N); i++) {
243
+ const chunk = await this.get(chunkPk(kind, source, manifest.Gen.S, i));
244
+ if (!chunk) {
245
+ const now = await this.get(sourcePk(kind, source));
246
+ if (now?.Gen.S !== manifest.Gen.S)
247
+ return null;
248
+ throw new DamagedListError(`email domain list ${sourcePk(kind, source)} is damaged (chunk ${i} of generation ${manifest.Gen.S} is missing); delete that item to reset the source`);
249
+ }
250
+ domains.push(...chunk.Domains.S.split('\n'));
251
+ }
252
+ return domains;
253
+ }
254
+ /** Best effort: a generation left behind here is reclaimed by a later write to the source. */
255
+ async dropGenerations(kind, source, generations) {
256
+ for (const { gen, chunks } of generations) {
257
+ try {
258
+ for (let i = 0; i < chunks; i++) {
259
+ await this.Client.send(new client_dynamodb_1.DeleteItemCommand({ TableName: this.Options.table, Key: { pk: s(chunkPk(kind, source, gen, i)) } }));
260
+ }
261
+ }
262
+ catch (err) {
263
+ this.Log.warn(err, `Could not drop generation ${gen} of email domain list ${kind}#${source}`);
264
+ }
265
+ }
266
+ }
267
+ async indexMembers() {
268
+ const index = await this.get(exports.INDEX_PK);
269
+ return index ? JSON.parse(index.Sources.S) : [];
270
+ }
271
+ async addToIndex(member) {
272
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
273
+ if (attempt > 0)
274
+ await sleep(BACKOFF_MS * attempt);
275
+ const index = await this.get(exports.INDEX_PK);
276
+ const members = new Set(index ? JSON.parse(index.Sources.S) : []);
277
+ if (members.has(member))
278
+ return;
279
+ members.add(member);
280
+ try {
281
+ await this.put({ pk: s(exports.INDEX_PK), Sources: s(JSON.stringify([...members].sort())), Ver: s((0, node_crypto_1.randomUUID)()) }, index ? equals('Ver', index.Ver) : notExists);
282
+ return;
283
+ }
284
+ catch (err) {
285
+ if (!isConditionFailure(err))
286
+ throw err;
287
+ }
288
+ }
289
+ throw new exceptions_1.InvalidOperation(`email domain list index kept changing during ${MAX_ATTEMPTS} writes`);
290
+ }
291
+ async get(pk, consistent = true) {
292
+ return (await this.Client.send(new client_dynamodb_1.GetItemCommand({ TableName: this.Options.table, Key: { pk: s(pk) }, ConsistentRead: consistent }))).Item;
293
+ }
294
+ async put(item, condition) {
295
+ await this.Client.send(new client_dynamodb_1.PutItemCommand({ TableName: this.Options.table, Item: item, ...condition }));
296
+ }
297
+ async ensureTable() {
298
+ const TableName = this.Options.table;
299
+ try {
300
+ await this.Client.send(new client_dynamodb_1.DescribeTableCommand({ TableName }));
301
+ }
302
+ catch (err) {
303
+ if (errorName(err) !== 'ResourceNotFoundException')
304
+ throw err;
305
+ await this.createTable();
306
+ }
307
+ await (0, client_dynamodb_1.waitUntilTableExists)({ client: this.Client, maxWaitTime: 120 }, { TableName });
308
+ const ttl = (await this.Client.send(new client_dynamodb_1.DescribeTimeToLiveCommand({ TableName }))).TimeToLiveDescription;
309
+ if (ttl?.TimeToLiveStatus === 'ENABLED' || ttl?.TimeToLiveStatus === 'ENABLING') {
310
+ if (ttl.AttributeName !== TTL_ATTRIBUTE)
311
+ this.Log.warn(`Table ${TableName} expires items by ${ttl.AttributeName}, not ${TTL_ATTRIBUTE}: expired verdicts stay in the table`);
312
+ return;
313
+ }
314
+ await this.enableTtl();
315
+ }
316
+ async createTable() {
317
+ try {
318
+ await this.Client.send(new client_dynamodb_1.CreateTableCommand({
319
+ TableName: this.Options.table,
320
+ AttributeDefinitions: [{ AttributeName: 'pk', AttributeType: 'S' }],
321
+ KeySchema: [{ AttributeName: 'pk', KeyType: 'HASH' }],
322
+ BillingMode: 'PAY_PER_REQUEST',
323
+ }));
324
+ }
325
+ catch (err) {
326
+ // Another instance booting at the same time created it first.
327
+ if (errorName(err) !== 'ResourceInUseException')
328
+ throw err;
329
+ }
330
+ }
331
+ async enableTtl() {
332
+ try {
333
+ await this.Client.send(new client_dynamodb_1.UpdateTimeToLiveCommand({ TableName: this.Options.table, TimeToLiveSpecification: { AttributeName: TTL_ATTRIBUTE, Enabled: true } }));
334
+ }
335
+ catch (err) {
336
+ // Another instance booting at the same time enabled it first.
337
+ if (errorName(err) !== 'ValidationException' || !/already enabled/i.test(err.message))
338
+ throw err;
339
+ }
340
+ }
341
+ };
342
+ exports.DynamoDbEmailVerificationStore = DynamoDbEmailVerificationStore;
343
+ __decorate([
344
+ (0, configuration_1.Config)('emailVerification.dynamodb'),
345
+ __metadata("design:type", Object)
346
+ ], DynamoDbEmailVerificationStore.prototype, "Options", void 0);
347
+ __decorate([
348
+ (0, log_1.Logger)('email-verification'),
349
+ __metadata("design:type", log_1.Log)
350
+ ], DynamoDbEmailVerificationStore.prototype, "Log", void 0);
351
+ exports.DynamoDbEmailVerificationStore = DynamoDbEmailVerificationStore = __decorate([
352
+ (0, di_1.Injectable)(email_verification_1.EmailVerificationStore)
353
+ ], DynamoDbEmailVerificationStore);
354
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,70 @@
1
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import type { AttributeValue, DynamoDBClientConfig } from '@aws-sdk/client-dynamodb';
3
+ import { EmailVerificationStore } from '@spinajs/email-verification';
4
+ import type { CachedVerdict, DynamoDbStoreOptions, ListEntry, ListKind } from '@spinajs/email-verification';
5
+ import { Log } from '@spinajs/log';
6
+ type Item = Record<string, AttributeValue>;
7
+ interface Condition {
8
+ ConditionExpression: string;
9
+ ExpressionAttributeNames: Record<string, string>;
10
+ ExpressionAttributeValues?: Item;
11
+ }
12
+ interface Generation {
13
+ gen: string;
14
+ chunks: number;
15
+ }
16
+ export declare const CHUNK_BYTES = 300000;
17
+ export declare const INDEX_PK = "S#index";
18
+ export declare function chunkByBytes(domains: readonly string[], maxBytes: number): string[][];
19
+ /**
20
+ * DynamoDB store (AWS SDK v3), one table with the string hash key `pk`. It uses no Scan.
21
+ * Each list source is a manifest (`S#<kind>#<source>`: live `Gen` and `Chunks` count, the
22
+ * replaced `PrevGen` and `PrevChunks`) over chunk items of newline-joined domains, plus an
23
+ * index item of the sources. A write puts a new generation of chunks, compare-and-sets the
24
+ * manifest, then deletes the replaced generation and the one before it, so the next write
25
+ * reclaims what a writer that died after its swap left behind. An emptied source keeps a
26
+ * manifest of zero chunks and its index member: removing either could race a writer
27
+ * re-creating the source. A reader that finds a chunk missing has raced a write, and reads
28
+ * the manifest again.
29
+ */
30
+ export declare class DynamoDbEmailVerificationStore extends EmailVerificationStore {
31
+ protected Options: DynamoDbStoreOptions;
32
+ protected Log: Log;
33
+ protected Client: DynamoDBClient;
34
+ resolve(): Promise<void>;
35
+ dispose(): Promise<void>;
36
+ getVerdict(verifier: string, domain: string): Promise<CachedVerdict | null>;
37
+ setVerdict(verifier: string, domain: string, verdict: CachedVerdict, ttlSeconds: number): Promise<void>;
38
+ getMeta(key: string): Promise<string | null>;
39
+ setMeta(key: string, value: string): Promise<void>;
40
+ listVersion(): Promise<string>;
41
+ loadLists(): Promise<ListEntry[]>;
42
+ upsertEntries(entries: readonly ListEntry[]): Promise<void>;
43
+ removeEntries(kind: ListKind, domains: readonly string[], source: string): Promise<void>;
44
+ replaceSource(source: string, kind: ListKind, domains: readonly string[]): Promise<void>;
45
+ protected createClient(cfg: DynamoDBClientConfig): DynamoDBClient;
46
+ protected mutateSource(kind: ListKind, source: string, change: (current: Set<string>) => Set<string>, readCurrent?: boolean): Promise<void>;
47
+ protected putChunks(kind: ListKind, source: string, gen: string, chunks: string[][]): Promise<void>;
48
+ /**
49
+ * true when the manifest now names `gen` (or named it before a later writer replaced it).
50
+ * A failure with an unknown outcome is rethrown and keeps the chunks, as the put may still land.
51
+ */
52
+ protected swapManifest(kind: ListKind, source: string, old: Item | undefined, gen: string, chunks: number): Promise<boolean>;
53
+ /** Runs every step even when one fails, then rethrows the first failure. */
54
+ protected settleSwap(kind: ListKind, source: string, replaced: Item | undefined, live: boolean): Promise<void>;
55
+ /** null when the source stayed unreadable through every retry. */
56
+ protected readSource(kind: ListKind, source: string): Promise<string[] | null>;
57
+ /** null when a chunk is gone because a writer replaced this generation during the read; throws when the manifest is unchanged, i.e. the data is damaged. */
58
+ protected readChunks(kind: ListKind, source: string, manifest: Item): Promise<string[] | null>;
59
+ /** Best effort: a generation left behind here is reclaimed by a later write to the source. */
60
+ protected dropGenerations(kind: ListKind, source: string, generations: Generation[]): Promise<void>;
61
+ protected indexMembers(): Promise<string[]>;
62
+ protected addToIndex(member: string): Promise<void>;
63
+ protected get(pk: string, consistent?: boolean): Promise<Item | undefined>;
64
+ protected put(item: Item, condition?: Condition): Promise<void>;
65
+ protected ensureTable(): Promise<void>;
66
+ protected createTable(): Promise<void>;
67
+ protected enableTtl(): Promise<void>;
68
+ }
69
+ export {};
70
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}