@keyv/dynamo 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/index.cjs +49 -4
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +55 -5
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -35,6 +35,76 @@ e.g:
|
|
|
35
35
|
const keyv = new KeyvDynamo({ tableName: 'cacheTable' });
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## Usage with NestJS
|
|
39
|
+
|
|
40
|
+
Since DynamoDB has a 400KB limit per item, compressing data can help in some cases.
|
|
41
|
+
|
|
42
|
+
### With a payload less than or equal to 400KB
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import { Keyv } from 'keyv'
|
|
46
|
+
import { KeyvDynamo } from '@keyv/dynamo'
|
|
47
|
+
import { DynamoModule } from '@lyfe/dynamo-module'
|
|
48
|
+
import { CacheModule } from '@nestjs/cache-manager'
|
|
49
|
+
import { Module } from '@nestjs/common'
|
|
50
|
+
|
|
51
|
+
@Module({
|
|
52
|
+
imports: [
|
|
53
|
+
CacheModule.registerAsync({
|
|
54
|
+
isGlobal: true,
|
|
55
|
+
useFactory: async () => {
|
|
56
|
+
return {
|
|
57
|
+
stores: [
|
|
58
|
+
new Keyv({
|
|
59
|
+
store: new KeyvDynamo({
|
|
60
|
+
tableName: 'TableName',
|
|
61
|
+
}),
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
exports: [DynamoModule],
|
|
69
|
+
})
|
|
70
|
+
export class InfrastructureModule {}
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### With a payload greater than 400KB
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
import { Keyv } from 'keyv'
|
|
78
|
+
import KeyvBrotli from '@keyv/compress-brotli'
|
|
79
|
+
import { KeyvDynamo } from '@keyv/dynamo'
|
|
80
|
+
import { DynamoModule } from '@lyfe/dynamo-module'
|
|
81
|
+
import { CacheModule } from '@nestjs/cache-manager'
|
|
82
|
+
import { Module } from '@nestjs/common'
|
|
83
|
+
|
|
84
|
+
@Module({
|
|
85
|
+
imports: [
|
|
86
|
+
CacheModule.registerAsync({
|
|
87
|
+
isGlobal: true,
|
|
88
|
+
useFactory: async () => {
|
|
89
|
+
return {
|
|
90
|
+
stores: [
|
|
91
|
+
new Keyv({
|
|
92
|
+
store: new KeyvDynamo({
|
|
93
|
+
tableName: 'TableName',
|
|
94
|
+
}),
|
|
95
|
+
compression: new KeyvBrotli(),
|
|
96
|
+
}),
|
|
97
|
+
],
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
],
|
|
102
|
+
exports: [DynamoModule],
|
|
103
|
+
})
|
|
104
|
+
export class InfrastructureModule {}
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
|
|
38
108
|
## License
|
|
39
109
|
|
|
40
110
|
[MIT © Jared Wray](LISCENCE)
|
package/dist/index.cjs
CHANGED
|
@@ -43,6 +43,7 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
43
43
|
namespace;
|
|
44
44
|
opts;
|
|
45
45
|
client;
|
|
46
|
+
tableReady;
|
|
46
47
|
constructor(options) {
|
|
47
48
|
super();
|
|
48
49
|
options ??= {};
|
|
@@ -55,14 +56,12 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
55
56
|
...options
|
|
56
57
|
};
|
|
57
58
|
this.client = import_lib_dynamodb.DynamoDBDocument.from(new import_client_dynamodb.DynamoDB(this.opts));
|
|
58
|
-
this.
|
|
59
|
+
this.tableReady = this.ensureTable(this.opts.tableName).catch((error) => {
|
|
59
60
|
this.emit("error", error);
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
|
-
async checkTableExists(tableName) {
|
|
63
|
-
await this.client.send(new import_client_dynamodb.DescribeTableCommand({ TableName: tableName }));
|
|
64
|
-
}
|
|
65
63
|
async set(key, value, ttl) {
|
|
64
|
+
await this.tableReady;
|
|
66
65
|
const sixHoursFromNowEpoch = Math.floor((Date.now() + this.sixHoursInMilliseconds) / 1e3);
|
|
67
66
|
const expiresAt = typeof ttl === "number" ? Math.floor((Date.now() + (ttl + 1e3)) / 1e3) : sixHoursFromNowEpoch;
|
|
68
67
|
const putInput = {
|
|
@@ -76,6 +75,7 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
76
75
|
await this.client.put(putInput);
|
|
77
76
|
}
|
|
78
77
|
async get(key) {
|
|
78
|
+
await this.tableReady;
|
|
79
79
|
const getInput = {
|
|
80
80
|
TableName: this.opts.tableName,
|
|
81
81
|
Key: {
|
|
@@ -86,6 +86,7 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
86
86
|
return Item?.value;
|
|
87
87
|
}
|
|
88
88
|
async delete(key) {
|
|
89
|
+
await this.tableReady;
|
|
89
90
|
const deleteInput = {
|
|
90
91
|
TableName: this.opts.tableName,
|
|
91
92
|
Key: {
|
|
@@ -97,6 +98,7 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
97
98
|
return Boolean(Attributes);
|
|
98
99
|
}
|
|
99
100
|
async getMany(keys) {
|
|
101
|
+
await this.tableReady;
|
|
100
102
|
const batchGetInput = {
|
|
101
103
|
RequestItems: {
|
|
102
104
|
[this.opts.tableName]: {
|
|
@@ -110,6 +112,7 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
110
112
|
return keys.map((key) => items.find((item) => item?.id === key)?.value);
|
|
111
113
|
}
|
|
112
114
|
async deleteMany(keys) {
|
|
115
|
+
await this.tableReady;
|
|
113
116
|
if (keys.length === 0) {
|
|
114
117
|
return false;
|
|
115
118
|
}
|
|
@@ -133,6 +136,7 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
133
136
|
return Boolean(response);
|
|
134
137
|
}
|
|
135
138
|
async clear() {
|
|
139
|
+
await this.tableReady;
|
|
136
140
|
const scanResult = await this.client.scan({
|
|
137
141
|
TableName: this.opts.tableName
|
|
138
142
|
});
|
|
@@ -142,6 +146,47 @@ var KeyvDynamo = class extends import_events.default {
|
|
|
142
146
|
extractKey(output, keyProperty = "id") {
|
|
143
147
|
return (output.Items ?? []).map((item) => item[keyProperty]).filter((key) => key.startsWith(this.namespace ?? ""));
|
|
144
148
|
}
|
|
149
|
+
async ensureTable(tableName) {
|
|
150
|
+
try {
|
|
151
|
+
await this.client.send(new import_client_dynamodb.DescribeTableCommand({ TableName: tableName }));
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error instanceof import_client_dynamodb.ResourceNotFoundException) {
|
|
154
|
+
await this.createTable(tableName);
|
|
155
|
+
} else {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async createTable(tableName) {
|
|
161
|
+
try {
|
|
162
|
+
await this.client.send(new import_client_dynamodb.CreateTableCommand({
|
|
163
|
+
TableName: tableName,
|
|
164
|
+
KeySchema: [{ AttributeName: "id", KeyType: "HASH" }],
|
|
165
|
+
AttributeDefinitions: [{ AttributeName: "id", AttributeType: "S" }],
|
|
166
|
+
BillingMode: "PAY_PER_REQUEST"
|
|
167
|
+
}));
|
|
168
|
+
await (0, import_client_dynamodb.waitUntilTableExists)(
|
|
169
|
+
{ client: this.client, maxWaitTime: 60 },
|
|
170
|
+
{ TableName: tableName }
|
|
171
|
+
);
|
|
172
|
+
await this.client.send(new import_client_dynamodb.UpdateTimeToLiveCommand({
|
|
173
|
+
TableName: tableName,
|
|
174
|
+
TimeToLiveSpecification: {
|
|
175
|
+
AttributeName: "expiresAt",
|
|
176
|
+
Enabled: true
|
|
177
|
+
}
|
|
178
|
+
}));
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (error instanceof import_client_dynamodb.ResourceInUseException) {
|
|
181
|
+
await (0, import_client_dynamodb.waitUntilTableExists)(
|
|
182
|
+
{ client: this.client, maxWaitTime: 60 },
|
|
183
|
+
{ TableName: tableName }
|
|
184
|
+
);
|
|
185
|
+
} else {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
145
190
|
};
|
|
146
191
|
var index_default = KeyvDynamo;
|
|
147
192
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -10,8 +10,8 @@ declare class KeyvDynamo extends EventEmitter implements KeyvStoreAdapter {
|
|
|
10
10
|
tableName: string;
|
|
11
11
|
};
|
|
12
12
|
private readonly client;
|
|
13
|
+
private readonly tableReady;
|
|
13
14
|
constructor(options: KeyvDynamoOptions | string);
|
|
14
|
-
checkTableExists(tableName: string): Promise<void>;
|
|
15
15
|
set(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
16
16
|
get<Value>(key: string): Promise<StoredData<Value>>;
|
|
17
17
|
delete(key: string): Promise<boolean>;
|
|
@@ -19,6 +19,8 @@ declare class KeyvDynamo extends EventEmitter implements KeyvStoreAdapter {
|
|
|
19
19
|
deleteMany(keys: string[]): Promise<boolean>;
|
|
20
20
|
clear(): Promise<void>;
|
|
21
21
|
private extractKey;
|
|
22
|
+
private ensureTable;
|
|
23
|
+
private createTable;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
type KeyvDynamoOptions = {
|
package/dist/index.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ declare class KeyvDynamo extends EventEmitter implements KeyvStoreAdapter {
|
|
|
10
10
|
tableName: string;
|
|
11
11
|
};
|
|
12
12
|
private readonly client;
|
|
13
|
+
private readonly tableReady;
|
|
13
14
|
constructor(options: KeyvDynamoOptions | string);
|
|
14
|
-
checkTableExists(tableName: string): Promise<void>;
|
|
15
15
|
set(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
16
16
|
get<Value>(key: string): Promise<StoredData<Value>>;
|
|
17
17
|
delete(key: string): Promise<boolean>;
|
|
@@ -19,6 +19,8 @@ declare class KeyvDynamo extends EventEmitter implements KeyvStoreAdapter {
|
|
|
19
19
|
deleteMany(keys: string[]): Promise<boolean>;
|
|
20
20
|
clear(): Promise<void>;
|
|
21
21
|
private extractKey;
|
|
22
|
+
private ensureTable;
|
|
23
|
+
private createTable;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
type KeyvDynamoOptions = {
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import EventEmitter from "events";
|
|
3
3
|
import {
|
|
4
|
+
CreateTableCommand,
|
|
4
5
|
DescribeTableCommand,
|
|
5
|
-
DynamoDB
|
|
6
|
+
DynamoDB,
|
|
7
|
+
ResourceNotFoundException,
|
|
8
|
+
ResourceInUseException,
|
|
9
|
+
UpdateTimeToLiveCommand,
|
|
10
|
+
waitUntilTableExists
|
|
6
11
|
} from "@aws-sdk/client-dynamodb";
|
|
7
12
|
import {
|
|
8
13
|
DynamoDBDocument
|
|
@@ -13,6 +18,7 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
13
18
|
namespace;
|
|
14
19
|
opts;
|
|
15
20
|
client;
|
|
21
|
+
tableReady;
|
|
16
22
|
constructor(options) {
|
|
17
23
|
super();
|
|
18
24
|
options ??= {};
|
|
@@ -25,14 +31,12 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
25
31
|
...options
|
|
26
32
|
};
|
|
27
33
|
this.client = DynamoDBDocument.from(new DynamoDB(this.opts));
|
|
28
|
-
this.
|
|
34
|
+
this.tableReady = this.ensureTable(this.opts.tableName).catch((error) => {
|
|
29
35
|
this.emit("error", error);
|
|
30
36
|
});
|
|
31
37
|
}
|
|
32
|
-
async checkTableExists(tableName) {
|
|
33
|
-
await this.client.send(new DescribeTableCommand({ TableName: tableName }));
|
|
34
|
-
}
|
|
35
38
|
async set(key, value, ttl) {
|
|
39
|
+
await this.tableReady;
|
|
36
40
|
const sixHoursFromNowEpoch = Math.floor((Date.now() + this.sixHoursInMilliseconds) / 1e3);
|
|
37
41
|
const expiresAt = typeof ttl === "number" ? Math.floor((Date.now() + (ttl + 1e3)) / 1e3) : sixHoursFromNowEpoch;
|
|
38
42
|
const putInput = {
|
|
@@ -46,6 +50,7 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
46
50
|
await this.client.put(putInput);
|
|
47
51
|
}
|
|
48
52
|
async get(key) {
|
|
53
|
+
await this.tableReady;
|
|
49
54
|
const getInput = {
|
|
50
55
|
TableName: this.opts.tableName,
|
|
51
56
|
Key: {
|
|
@@ -56,6 +61,7 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
56
61
|
return Item?.value;
|
|
57
62
|
}
|
|
58
63
|
async delete(key) {
|
|
64
|
+
await this.tableReady;
|
|
59
65
|
const deleteInput = {
|
|
60
66
|
TableName: this.opts.tableName,
|
|
61
67
|
Key: {
|
|
@@ -67,6 +73,7 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
67
73
|
return Boolean(Attributes);
|
|
68
74
|
}
|
|
69
75
|
async getMany(keys) {
|
|
76
|
+
await this.tableReady;
|
|
70
77
|
const batchGetInput = {
|
|
71
78
|
RequestItems: {
|
|
72
79
|
[this.opts.tableName]: {
|
|
@@ -80,6 +87,7 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
80
87
|
return keys.map((key) => items.find((item) => item?.id === key)?.value);
|
|
81
88
|
}
|
|
82
89
|
async deleteMany(keys) {
|
|
90
|
+
await this.tableReady;
|
|
83
91
|
if (keys.length === 0) {
|
|
84
92
|
return false;
|
|
85
93
|
}
|
|
@@ -103,6 +111,7 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
103
111
|
return Boolean(response);
|
|
104
112
|
}
|
|
105
113
|
async clear() {
|
|
114
|
+
await this.tableReady;
|
|
106
115
|
const scanResult = await this.client.scan({
|
|
107
116
|
TableName: this.opts.tableName
|
|
108
117
|
});
|
|
@@ -112,6 +121,47 @@ var KeyvDynamo = class extends EventEmitter {
|
|
|
112
121
|
extractKey(output, keyProperty = "id") {
|
|
113
122
|
return (output.Items ?? []).map((item) => item[keyProperty]).filter((key) => key.startsWith(this.namespace ?? ""));
|
|
114
123
|
}
|
|
124
|
+
async ensureTable(tableName) {
|
|
125
|
+
try {
|
|
126
|
+
await this.client.send(new DescribeTableCommand({ TableName: tableName }));
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof ResourceNotFoundException) {
|
|
129
|
+
await this.createTable(tableName);
|
|
130
|
+
} else {
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async createTable(tableName) {
|
|
136
|
+
try {
|
|
137
|
+
await this.client.send(new CreateTableCommand({
|
|
138
|
+
TableName: tableName,
|
|
139
|
+
KeySchema: [{ AttributeName: "id", KeyType: "HASH" }],
|
|
140
|
+
AttributeDefinitions: [{ AttributeName: "id", AttributeType: "S" }],
|
|
141
|
+
BillingMode: "PAY_PER_REQUEST"
|
|
142
|
+
}));
|
|
143
|
+
await waitUntilTableExists(
|
|
144
|
+
{ client: this.client, maxWaitTime: 60 },
|
|
145
|
+
{ TableName: tableName }
|
|
146
|
+
);
|
|
147
|
+
await this.client.send(new UpdateTimeToLiveCommand({
|
|
148
|
+
TableName: tableName,
|
|
149
|
+
TimeToLiveSpecification: {
|
|
150
|
+
AttributeName: "expiresAt",
|
|
151
|
+
Enabled: true
|
|
152
|
+
}
|
|
153
|
+
}));
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error instanceof ResourceInUseException) {
|
|
156
|
+
await waitUntilTableExists(
|
|
157
|
+
{ client: this.client, maxWaitTime: 60 },
|
|
158
|
+
{ TableName: tableName }
|
|
159
|
+
);
|
|
160
|
+
} else {
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
115
165
|
};
|
|
116
166
|
var index_default = KeyvDynamo;
|
|
117
167
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyv/dynamo",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "DynamoDB storage adapter for Keyv",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -58,13 +58,13 @@
|
|
|
58
58
|
},
|
|
59
59
|
"homepage": "https://github.com/jaredwray/keyv",
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@aws-sdk/client-dynamodb": "^3.
|
|
62
|
-
"@aws-sdk/lib-dynamodb": "^3.
|
|
61
|
+
"@aws-sdk/client-dynamodb": "^3.859.0",
|
|
62
|
+
"@aws-sdk/lib-dynamodb": "^3.859.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"vitest": "^3.2.4",
|
|
66
|
-
"keyv": "^
|
|
67
|
-
"
|
|
66
|
+
"@keyv/test-suite": "^2.1.1",
|
|
67
|
+
"keyv": "^5.5.0"
|
|
68
68
|
},
|
|
69
69
|
"tsd": {
|
|
70
70
|
"directory": "test"
|