@model-ts/dynamodb 4.0.0 → 4.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/CHANGELOG.md +12 -0
- package/dist/cjs/__test__/client-env-guard.test.d.ts +1 -0
- package/dist/cjs/__test__/client-env-guard.test.js +28 -0
- package/dist/cjs/__test__/client-env-guard.test.js.map +1 -0
- package/dist/cjs/__test__/conformance.test.d.ts +1 -0
- package/dist/cjs/__test__/conformance.test.js +1835 -0
- package/dist/cjs/__test__/conformance.test.js.map +1 -0
- package/dist/cjs/__test__/in-memory.spec.test.d.ts +1 -0
- package/dist/cjs/__test__/in-memory.spec.test.js +185 -0
- package/dist/cjs/__test__/in-memory.spec.test.js.map +1 -0
- package/dist/cjs/__test__/rollback.test.d.ts +1 -0
- package/dist/cjs/__test__/rollback.test.js +196 -0
- package/dist/cjs/__test__/rollback.test.js.map +1 -0
- package/dist/cjs/client.d.ts +2 -2
- package/dist/cjs/client.js +14 -6
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/errors.d.ts +13 -1
- package/dist/cjs/errors.js +12 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/in-memory/document-client.d.ts +45 -0
- package/dist/cjs/in-memory/document-client.js +795 -0
- package/dist/cjs/in-memory/document-client.js.map +1 -0
- package/dist/cjs/in-memory/expression.d.ts +42 -0
- package/dist/cjs/in-memory/expression.js +585 -0
- package/dist/cjs/in-memory/expression.js.map +1 -0
- package/dist/cjs/in-memory/index.d.ts +2 -0
- package/dist/cjs/in-memory/index.js +6 -0
- package/dist/cjs/in-memory/index.js.map +1 -0
- package/dist/cjs/in-memory/spec.d.ts +23 -0
- package/dist/cjs/in-memory/spec.js +141 -0
- package/dist/cjs/in-memory/spec.js.map +1 -0
- package/dist/cjs/in-memory/store.d.ts +73 -0
- package/dist/cjs/in-memory/store.js +267 -0
- package/dist/cjs/in-memory/store.js.map +1 -0
- package/dist/cjs/in-memory/treap.d.ts +31 -0
- package/dist/cjs/in-memory/treap.js +187 -0
- package/dist/cjs/in-memory/treap.js.map +1 -0
- package/dist/cjs/in-memory/utils.d.ts +10 -0
- package/dist/cjs/in-memory/utils.js +38 -0
- package/dist/cjs/in-memory/utils.js.map +1 -0
- package/dist/cjs/sandbox.d.ts +2 -0
- package/dist/cjs/sandbox.js +172 -1
- package/dist/cjs/sandbox.js.map +1 -1
- package/dist/esm/__test__/client-env-guard.test.d.ts +1 -0
- package/dist/esm/__test__/client-env-guard.test.js +26 -0
- package/dist/esm/__test__/client-env-guard.test.js.map +1 -0
- package/dist/esm/__test__/conformance.test.d.ts +1 -0
- package/dist/esm/__test__/conformance.test.js +1833 -0
- package/dist/esm/__test__/conformance.test.js.map +1 -0
- package/dist/esm/__test__/in-memory.spec.test.d.ts +1 -0
- package/dist/esm/__test__/in-memory.spec.test.js +183 -0
- package/dist/esm/__test__/in-memory.spec.test.js.map +1 -0
- package/dist/esm/__test__/rollback.test.d.ts +1 -0
- package/dist/esm/__test__/rollback.test.js +194 -0
- package/dist/esm/__test__/rollback.test.js.map +1 -0
- package/dist/esm/client.d.ts +2 -2
- package/dist/esm/client.js +14 -6
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.d.ts +13 -1
- package/dist/esm/errors.js +10 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/in-memory/document-client.d.ts +45 -0
- package/dist/esm/in-memory/document-client.js +791 -0
- package/dist/esm/in-memory/document-client.js.map +1 -0
- package/dist/esm/in-memory/expression.d.ts +42 -0
- package/dist/esm/in-memory/expression.js +577 -0
- package/dist/esm/in-memory/expression.js.map +1 -0
- package/dist/esm/in-memory/index.d.ts +2 -0
- package/dist/esm/in-memory/index.js +3 -0
- package/dist/esm/in-memory/index.js.map +1 -0
- package/dist/esm/in-memory/spec.d.ts +23 -0
- package/dist/esm/in-memory/spec.js +138 -0
- package/dist/esm/in-memory/spec.js.map +1 -0
- package/dist/esm/in-memory/store.d.ts +73 -0
- package/dist/esm/in-memory/store.js +258 -0
- package/dist/esm/in-memory/store.js.map +1 -0
- package/dist/esm/in-memory/treap.d.ts +31 -0
- package/dist/esm/in-memory/treap.js +183 -0
- package/dist/esm/in-memory/treap.js.map +1 -0
- package/dist/esm/in-memory/utils.d.ts +10 -0
- package/dist/esm/in-memory/utils.js +28 -0
- package/dist/esm/in-memory/utils.js.map +1 -0
- package/dist/esm/sandbox.d.ts +2 -0
- package/dist/esm/sandbox.js +172 -1
- package/dist/esm/sandbox.js.map +1 -1
- package/package.json +2 -1
- package/src/__test__/client-env-guard.test.ts +31 -0
- package/src/__test__/conformance.test.ts +2042 -0
- package/src/__test__/in-memory.spec.test.ts +230 -0
- package/src/__test__/rollback.test.ts +279 -0
- package/src/client.ts +17 -4
- package/src/errors.ts +24 -0
- package/src/in-memory/document-client.ts +1140 -0
- package/src/in-memory/expression.ts +730 -0
- package/src/in-memory/index.ts +2 -0
- package/src/in-memory/spec.ts +159 -0
- package/src/in-memory/store.ts +360 -0
- package/src/in-memory/treap.ts +239 -0
- package/src/in-memory/utils.ts +45 -0
- package/src/sandbox.ts +227 -1
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { NotSupportedError } from "../errors";
|
|
3
|
+
import { evaluateConditionExpression, parseKeyConditionExpression, parseUpdateExpression, } from "./expression";
|
|
4
|
+
import { InMemoryTableState, PRIMARY_INDEX_NAME, isSupportedIndexName, matchesKeyConditionDescriptor, parseIndexName, isGSI, } from "./store";
|
|
5
|
+
import { IN_MEMORY_SPEC } from "./spec";
|
|
6
|
+
import { cloneItem, encodeItemKey } from "./utils";
|
|
7
|
+
class InMemoryDocumentClientImpl {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.tables = new Map();
|
|
10
|
+
}
|
|
11
|
+
get(params) {
|
|
12
|
+
return this.request("get", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
this.assertSupportedParams("get", params);
|
|
14
|
+
const tableName = this.getRequiredTableName(params);
|
|
15
|
+
const key = this.getRequiredKey(params, "Key");
|
|
16
|
+
const table = this.getTable(tableName);
|
|
17
|
+
const item = table.cloneItemByKey(key);
|
|
18
|
+
return item ? { Item: item } : {};
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
put(params) {
|
|
22
|
+
return this.request("put", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
this.assertSupportedParams("put", params);
|
|
24
|
+
const tableName = this.getRequiredTableName(params);
|
|
25
|
+
const table = this.getTable(tableName);
|
|
26
|
+
const item = params.Item;
|
|
27
|
+
if (!item || typeof item !== "object") {
|
|
28
|
+
throw new NotSupportedError({
|
|
29
|
+
method: "put",
|
|
30
|
+
featurePath: "put.Item",
|
|
31
|
+
reason: "Item must be an object.",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
this.assertPrimaryKey(item, "put");
|
|
35
|
+
this.assertExpressionAttributeInputs(params, [params.ConditionExpression]);
|
|
36
|
+
const existing = table.cloneItemByKey({ PK: item.PK, SK: item.SK });
|
|
37
|
+
this.assertCondition("put", params, existing);
|
|
38
|
+
table.put(item);
|
|
39
|
+
return {};
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
update(params) {
|
|
43
|
+
return this.request("update", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
var _a;
|
|
45
|
+
this.assertSupportedParams("update", params);
|
|
46
|
+
const tableName = this.getRequiredTableName(params);
|
|
47
|
+
const key = this.getRequiredKey(params, "Key");
|
|
48
|
+
if (typeof params.UpdateExpression !== "string") {
|
|
49
|
+
throw new NotSupportedError({
|
|
50
|
+
method: "update",
|
|
51
|
+
featurePath: "update.UpdateExpression",
|
|
52
|
+
reason: "UpdateExpression is required.",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const returnValues = (_a = params.ReturnValues) !== null && _a !== void 0 ? _a : "NONE";
|
|
56
|
+
if (!["NONE", "ALL_NEW"].includes(returnValues)) {
|
|
57
|
+
throw this.validationError(`Unsupported ReturnValues value: ${returnValues}`);
|
|
58
|
+
}
|
|
59
|
+
this.assertExpressionAttributeInputs(params, [
|
|
60
|
+
params.ConditionExpression,
|
|
61
|
+
params.UpdateExpression,
|
|
62
|
+
]);
|
|
63
|
+
const table = this.getTable(tableName);
|
|
64
|
+
const existing = table.cloneItemByKey(key);
|
|
65
|
+
this.assertCondition("update", params, existing);
|
|
66
|
+
const base = existing !== null && existing !== void 0 ? existing : { PK: key.PK, SK: key.SK };
|
|
67
|
+
const parsed = this.parseUpdateExpressionOrValidationError(params.UpdateExpression, {
|
|
68
|
+
method: "update",
|
|
69
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
70
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
71
|
+
item: base,
|
|
72
|
+
});
|
|
73
|
+
const next = cloneItem(base);
|
|
74
|
+
for (const assignment of parsed.set) {
|
|
75
|
+
if ((assignment.attribute === "PK" || assignment.attribute === "SK") && assignment.value !== next[assignment.attribute]) {
|
|
76
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${assignment.attribute}. This attribute is part of the key`);
|
|
77
|
+
}
|
|
78
|
+
next[assignment.attribute] = assignment.value;
|
|
79
|
+
}
|
|
80
|
+
for (const attribute of parsed.remove) {
|
|
81
|
+
if (attribute === "PK" || attribute === "SK") {
|
|
82
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${attribute}. This attribute is part of the key`);
|
|
83
|
+
}
|
|
84
|
+
delete next[attribute];
|
|
85
|
+
}
|
|
86
|
+
this.assertPrimaryKey(next, "update");
|
|
87
|
+
table.put(next);
|
|
88
|
+
if (returnValues === "ALL_NEW") {
|
|
89
|
+
return { Attributes: cloneItem(next) };
|
|
90
|
+
}
|
|
91
|
+
return {};
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
delete(params) {
|
|
95
|
+
return this.request("delete", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
this.assertSupportedParams("delete", params);
|
|
97
|
+
const tableName = this.getRequiredTableName(params);
|
|
98
|
+
const key = this.getRequiredKey(params, "Key");
|
|
99
|
+
const table = this.getTable(tableName);
|
|
100
|
+
const existing = table.cloneItemByKey(key);
|
|
101
|
+
this.assertExpressionAttributeInputs(params, [params.ConditionExpression]);
|
|
102
|
+
this.assertCondition("delete", params, existing);
|
|
103
|
+
table.deleteByKey(key);
|
|
104
|
+
return {};
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
query(params) {
|
|
108
|
+
return this.request("query", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
var _a;
|
|
110
|
+
this.assertSupportedParams("query", params);
|
|
111
|
+
const tableName = this.getRequiredTableName(params);
|
|
112
|
+
const table = this.getTable(tableName);
|
|
113
|
+
if (typeof params.KeyConditionExpression !== "string") {
|
|
114
|
+
throw new NotSupportedError({
|
|
115
|
+
method: "query",
|
|
116
|
+
featurePath: "query.KeyConditionExpression",
|
|
117
|
+
reason: "KeyConditionExpression is required.",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const indexName = this.resolveIndexName(params.IndexName);
|
|
121
|
+
if (params.ConsistentRead && isGSI(indexName)) {
|
|
122
|
+
throw this.validationError("Consistent read cannot be true when querying a GSI");
|
|
123
|
+
}
|
|
124
|
+
this.assertExpressionAttributeInputs(params, [
|
|
125
|
+
params.KeyConditionExpression,
|
|
126
|
+
params.FilterExpression,
|
|
127
|
+
]);
|
|
128
|
+
const condition = this.parseKeyConditionOrValidationError(params.KeyConditionExpression, {
|
|
129
|
+
method: "query",
|
|
130
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
131
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
132
|
+
});
|
|
133
|
+
if (!matchesKeyConditionDescriptor(indexName, condition)) {
|
|
134
|
+
throw new NotSupportedError({
|
|
135
|
+
method: "query",
|
|
136
|
+
featurePath: "query.KeyConditionExpression",
|
|
137
|
+
reason: "KeyConditionExpression attributes do not match the selected index.",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (typeof condition.hashValue !== "string") {
|
|
141
|
+
throw new NotSupportedError({
|
|
142
|
+
method: "query",
|
|
143
|
+
featurePath: "query.KeyConditionExpression.partitionValue",
|
|
144
|
+
reason: "Partition key values must be strings.",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const scanIndexForward = params.ScanIndexForward !== false;
|
|
148
|
+
const limit = this.normalizeLimit(params.Limit, "query");
|
|
149
|
+
const exclusiveStartKey = params.ExclusiveStartKey
|
|
150
|
+
? this.resolveExclusiveStartKey(params.ExclusiveStartKey, indexName, table)
|
|
151
|
+
: undefined;
|
|
152
|
+
const iterator = table.iterateQueryCandidates({
|
|
153
|
+
indexName,
|
|
154
|
+
hashKey: condition.hashValue,
|
|
155
|
+
rangeCondition: (_a = condition.range) === null || _a === void 0 ? void 0 : _a.condition,
|
|
156
|
+
scanIndexForward,
|
|
157
|
+
exclusiveStartKey,
|
|
158
|
+
})[Symbol.iterator]();
|
|
159
|
+
const items = [];
|
|
160
|
+
let scannedCount = 0;
|
|
161
|
+
let lastEvaluatedKey;
|
|
162
|
+
let next = iterator.next();
|
|
163
|
+
while (!next.done) {
|
|
164
|
+
const candidate = next.value;
|
|
165
|
+
scannedCount += 1;
|
|
166
|
+
const include = params.FilterExpression
|
|
167
|
+
? this.evaluateConditionOrValidationError(params.FilterExpression, candidate.item, {
|
|
168
|
+
method: "query",
|
|
169
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
170
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
171
|
+
}, "FilterExpression")
|
|
172
|
+
: true;
|
|
173
|
+
if (include) {
|
|
174
|
+
items.push(candidate.item);
|
|
175
|
+
}
|
|
176
|
+
if (limit !== undefined && scannedCount >= limit) {
|
|
177
|
+
lastEvaluatedKey = this.buildLastEvaluatedKey(indexName, candidate.item);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
next = iterator.next();
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
Items: items,
|
|
184
|
+
Count: items.length,
|
|
185
|
+
ScannedCount: scannedCount,
|
|
186
|
+
LastEvaluatedKey: lastEvaluatedKey,
|
|
187
|
+
};
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
scan(params) {
|
|
191
|
+
return this.request("scan", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
192
|
+
this.assertSupportedParams("scan", params);
|
|
193
|
+
const tableName = this.getRequiredTableName(params);
|
|
194
|
+
const table = this.getTable(tableName);
|
|
195
|
+
const limit = this.normalizeLimit(params.Limit, "scan");
|
|
196
|
+
this.assertExpressionAttributeInputs(params, [params.FilterExpression]);
|
|
197
|
+
const exclusiveStartKey = params.ExclusiveStartKey
|
|
198
|
+
? this.getRequiredKey({ Key: params.ExclusiveStartKey }, "ExclusiveStartKey")
|
|
199
|
+
: undefined;
|
|
200
|
+
const all = table.scanItems(exclusiveStartKey);
|
|
201
|
+
const items = [];
|
|
202
|
+
let scannedCount = 0;
|
|
203
|
+
let lastEvaluatedKey;
|
|
204
|
+
for (let index = 0; index < all.length; index += 1) {
|
|
205
|
+
const candidate = all[index];
|
|
206
|
+
scannedCount += 1;
|
|
207
|
+
const include = params.FilterExpression
|
|
208
|
+
? this.evaluateConditionOrValidationError(params.FilterExpression, candidate, {
|
|
209
|
+
method: "scan",
|
|
210
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
211
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
212
|
+
}, "FilterExpression")
|
|
213
|
+
: true;
|
|
214
|
+
if (include) {
|
|
215
|
+
items.push(candidate);
|
|
216
|
+
}
|
|
217
|
+
if (limit !== undefined && scannedCount >= limit) {
|
|
218
|
+
lastEvaluatedKey = { PK: candidate.PK, SK: candidate.SK };
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
Items: items,
|
|
224
|
+
Count: items.length,
|
|
225
|
+
ScannedCount: scannedCount,
|
|
226
|
+
LastEvaluatedKey: lastEvaluatedKey,
|
|
227
|
+
};
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
batchGet(params) {
|
|
231
|
+
return this.request("batchGet", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
232
|
+
this.assertSupportedParams("batchGet", params);
|
|
233
|
+
if (!params.RequestItems || typeof params.RequestItems !== "object") {
|
|
234
|
+
throw new NotSupportedError({
|
|
235
|
+
method: "batchGet",
|
|
236
|
+
featurePath: "batchGet.RequestItems",
|
|
237
|
+
reason: "RequestItems is required.",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const responses = {};
|
|
241
|
+
for (const [tableName, request] of Object.entries(params.RequestItems)) {
|
|
242
|
+
if (!request || typeof request !== "object") {
|
|
243
|
+
throw new NotSupportedError({
|
|
244
|
+
method: "batchGet",
|
|
245
|
+
featurePath: "batchGet.RequestItems",
|
|
246
|
+
reason: "Each RequestItems entry must be an object.",
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
const keys = Array.isArray(request.Keys) ? request.Keys : [];
|
|
250
|
+
if (keys.length > 100) {
|
|
251
|
+
throw this.validationError("Too many items requested for the BatchGetItem call");
|
|
252
|
+
}
|
|
253
|
+
const table = this.getTable(tableName);
|
|
254
|
+
const tableResponses = [];
|
|
255
|
+
const unique = new Set();
|
|
256
|
+
for (const key of keys) {
|
|
257
|
+
const parsedKey = this.getRequiredKey({ Key: key }, "Key");
|
|
258
|
+
const dedupKey = `${parsedKey.PK}::${parsedKey.SK}`;
|
|
259
|
+
if (unique.has(dedupKey)) {
|
|
260
|
+
throw this.validationError("Provided list of item keys contains duplicates");
|
|
261
|
+
}
|
|
262
|
+
unique.add(dedupKey);
|
|
263
|
+
const item = table.cloneItemByKey(parsedKey);
|
|
264
|
+
if (item)
|
|
265
|
+
tableResponses.push(item);
|
|
266
|
+
}
|
|
267
|
+
responses[tableName] = tableResponses;
|
|
268
|
+
}
|
|
269
|
+
return { Responses: responses };
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
batchWrite(params) {
|
|
273
|
+
return this.request("batchWrite", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
274
|
+
this.assertSupportedParams("batchWrite", params);
|
|
275
|
+
if (!params.RequestItems || typeof params.RequestItems !== "object") {
|
|
276
|
+
throw new NotSupportedError({
|
|
277
|
+
method: "batchWrite",
|
|
278
|
+
featurePath: "batchWrite.RequestItems",
|
|
279
|
+
reason: "RequestItems is required.",
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
for (const [tableName, requests] of Object.entries(params.RequestItems)) {
|
|
283
|
+
if (!Array.isArray(requests)) {
|
|
284
|
+
throw new NotSupportedError({
|
|
285
|
+
method: "batchWrite",
|
|
286
|
+
featurePath: "batchWrite.RequestItems",
|
|
287
|
+
reason: "Each RequestItems entry must be an array.",
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
if (requests.length > 25) {
|
|
291
|
+
throw this.validationError("Too many items requested for the BatchWriteItem call");
|
|
292
|
+
}
|
|
293
|
+
const table = this.getTable(tableName);
|
|
294
|
+
for (const entry of requests) {
|
|
295
|
+
if (entry.PutRequest) {
|
|
296
|
+
const item = entry.PutRequest.Item;
|
|
297
|
+
if (!item || typeof item !== "object") {
|
|
298
|
+
throw new NotSupportedError({
|
|
299
|
+
method: "batchWrite",
|
|
300
|
+
featurePath: "batchWrite.RequestItems.PutRequest.Item",
|
|
301
|
+
reason: "PutRequest.Item must be an object.",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
this.assertPrimaryKey(item, "batchWrite");
|
|
305
|
+
table.put(item);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (entry.DeleteRequest) {
|
|
309
|
+
const key = this.getRequiredKey({ Key: entry.DeleteRequest.Key }, "DeleteRequest.Key");
|
|
310
|
+
table.deleteByKey(key);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
throw this.validationError("Supplied AttributeValue has more than one datatypes set, must contain exactly one of the supported datatypes");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return { UnprocessedItems: {} };
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
transactWrite(params) {
|
|
320
|
+
return this.request("transactWrite", params, () => __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
var _a, _b;
|
|
322
|
+
this.assertSupportedParams("transactWrite", params);
|
|
323
|
+
const transactItems = params.TransactItems;
|
|
324
|
+
if (!Array.isArray(transactItems) || transactItems.length === 0) {
|
|
325
|
+
throw new NotSupportedError({
|
|
326
|
+
method: "transactWrite",
|
|
327
|
+
featurePath: "transactWrite.TransactItems",
|
|
328
|
+
reason: "TransactItems must be a non-empty array.",
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (transactItems.length > 100) {
|
|
332
|
+
throw this.validationError("Member must have length less than or equal to 100");
|
|
333
|
+
}
|
|
334
|
+
const touched = new Set();
|
|
335
|
+
const journal = new Map();
|
|
336
|
+
const remember = (tableName, key, before) => {
|
|
337
|
+
const marker = `${tableName}::${key.PK}::${key.SK}`;
|
|
338
|
+
if (!journal.has(marker)) {
|
|
339
|
+
journal.set(marker, {
|
|
340
|
+
tableName,
|
|
341
|
+
key,
|
|
342
|
+
before: before ? cloneItem(before) : null,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
const ensureNoDuplicate = (tableName, key) => {
|
|
347
|
+
const marker = `${tableName}::${key.PK}::${key.SK}`;
|
|
348
|
+
if (touched.has(marker)) {
|
|
349
|
+
throw this.validationError("Transaction request cannot include multiple operations on one item");
|
|
350
|
+
}
|
|
351
|
+
touched.add(marker);
|
|
352
|
+
};
|
|
353
|
+
const rollback = () => {
|
|
354
|
+
for (const entry of [...journal.values()].reverse()) {
|
|
355
|
+
const table = this.getTable(entry.tableName);
|
|
356
|
+
if (entry.before === null) {
|
|
357
|
+
table.deleteByKey(entry.key);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
table.put(entry.before);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
try {
|
|
365
|
+
for (const transactionEntry of transactItems) {
|
|
366
|
+
if (transactionEntry.Put) {
|
|
367
|
+
const put = transactionEntry.Put;
|
|
368
|
+
const tableName = this.getRequiredTableName(put);
|
|
369
|
+
const table = this.getTable(tableName);
|
|
370
|
+
const item = put.Item;
|
|
371
|
+
if (!item || typeof item !== "object") {
|
|
372
|
+
throw new NotSupportedError({
|
|
373
|
+
method: "transactWrite",
|
|
374
|
+
featurePath: "transactWrite.TransactItems.Put.Item",
|
|
375
|
+
reason: "Put.Item must be an object.",
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
this.assertPrimaryKey(item, "transactWrite");
|
|
379
|
+
const key = { PK: item.PK, SK: item.SK };
|
|
380
|
+
ensureNoDuplicate(tableName, key);
|
|
381
|
+
const existing = table.cloneItemByKey(key);
|
|
382
|
+
this.assertExpressionAttributeInputs(put, [put.ConditionExpression]);
|
|
383
|
+
this.assertCondition("transactWrite", put, existing);
|
|
384
|
+
remember(tableName, key, existing);
|
|
385
|
+
table.put(item);
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (transactionEntry.Update) {
|
|
389
|
+
const update = transactionEntry.Update;
|
|
390
|
+
const tableName = this.getRequiredTableName(update);
|
|
391
|
+
const key = this.getRequiredKey(update, "Key");
|
|
392
|
+
ensureNoDuplicate(tableName, key);
|
|
393
|
+
const table = this.getTable(tableName);
|
|
394
|
+
const existing = table.cloneItemByKey(key);
|
|
395
|
+
this.assertExpressionAttributeInputs(update, [
|
|
396
|
+
update.ConditionExpression,
|
|
397
|
+
update.UpdateExpression,
|
|
398
|
+
]);
|
|
399
|
+
this.assertCondition("transactWrite", update, existing);
|
|
400
|
+
if (typeof update.UpdateExpression !== "string") {
|
|
401
|
+
throw new NotSupportedError({
|
|
402
|
+
method: "transactWrite",
|
|
403
|
+
featurePath: "transactWrite.TransactItems.Update.UpdateExpression",
|
|
404
|
+
reason: "UpdateExpression is required.",
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
const base = existing !== null && existing !== void 0 ? existing : { PK: key.PK, SK: key.SK };
|
|
408
|
+
const parsed = this.parseUpdateExpressionOrValidationError(update.UpdateExpression, {
|
|
409
|
+
method: "transactWrite",
|
|
410
|
+
expressionAttributeNames: update.ExpressionAttributeNames,
|
|
411
|
+
expressionAttributeValues: update.ExpressionAttributeValues,
|
|
412
|
+
item: base,
|
|
413
|
+
});
|
|
414
|
+
const next = cloneItem(base);
|
|
415
|
+
for (const assignment of parsed.set) {
|
|
416
|
+
if ((assignment.attribute === "PK" || assignment.attribute === "SK") && assignment.value !== next[assignment.attribute]) {
|
|
417
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${assignment.attribute}. This attribute is part of the key`);
|
|
418
|
+
}
|
|
419
|
+
next[assignment.attribute] = assignment.value;
|
|
420
|
+
}
|
|
421
|
+
for (const attribute of parsed.remove) {
|
|
422
|
+
if (attribute === "PK" || attribute === "SK") {
|
|
423
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${attribute}. This attribute is part of the key`);
|
|
424
|
+
}
|
|
425
|
+
delete next[attribute];
|
|
426
|
+
}
|
|
427
|
+
this.assertPrimaryKey(next, "transactWrite");
|
|
428
|
+
remember(tableName, key, existing);
|
|
429
|
+
table.put(next);
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (transactionEntry.Delete) {
|
|
433
|
+
const del = transactionEntry.Delete;
|
|
434
|
+
const tableName = this.getRequiredTableName(del);
|
|
435
|
+
const key = this.getRequiredKey(del, "Key");
|
|
436
|
+
ensureNoDuplicate(tableName, key);
|
|
437
|
+
const table = this.getTable(tableName);
|
|
438
|
+
const existing = table.cloneItemByKey(key);
|
|
439
|
+
this.assertExpressionAttributeInputs(del, [del.ConditionExpression]);
|
|
440
|
+
this.assertCondition("transactWrite", del, existing);
|
|
441
|
+
remember(tableName, key, existing);
|
|
442
|
+
table.deleteByKey(key);
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (transactionEntry.ConditionCheck) {
|
|
446
|
+
const check = transactionEntry.ConditionCheck;
|
|
447
|
+
const tableName = this.getRequiredTableName(check);
|
|
448
|
+
const key = this.getRequiredKey(check, "Key");
|
|
449
|
+
ensureNoDuplicate(tableName, key);
|
|
450
|
+
const table = this.getTable(tableName);
|
|
451
|
+
const existing = table.cloneItemByKey(key);
|
|
452
|
+
this.assertExpressionAttributeInputs(check, [check.ConditionExpression]);
|
|
453
|
+
this.assertCondition("transactWrite", check, existing);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
throw new NotSupportedError({
|
|
457
|
+
method: "transactWrite",
|
|
458
|
+
featurePath: "transactWrite.TransactItems",
|
|
459
|
+
reason: "Each transaction entry must include Put, Update, Delete, or ConditionCheck.",
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
rollback();
|
|
465
|
+
if (error instanceof NotSupportedError)
|
|
466
|
+
throw error;
|
|
467
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "ValidationException" &&
|
|
468
|
+
/Cannot update attribute (PK|SK)\. This attribute is part of the key/.test(String((_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : ""))) {
|
|
469
|
+
throw this.transactionCanceledError("Transaction cancelled, please refer cancellation reasons for specific reasons [ValidationError]");
|
|
470
|
+
}
|
|
471
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "ValidationException") {
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "ConditionalCheckFailedException") {
|
|
475
|
+
throw this.transactionCanceledError("Transaction cancelled, please refer cancellation reasons for specific reasons [None, ConditionalCheckFailed]");
|
|
476
|
+
}
|
|
477
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "TransactionCanceledException") {
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
throw this.transactionCanceledError((_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : "Transaction failed.");
|
|
481
|
+
}
|
|
482
|
+
return {};
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
__inMemorySnapshot(tableName) {
|
|
486
|
+
return this.getTable(tableName).snapshot();
|
|
487
|
+
}
|
|
488
|
+
__inMemoryResetTable(tableName) {
|
|
489
|
+
this.getTable(tableName).clear();
|
|
490
|
+
}
|
|
491
|
+
request(method, params, fn) {
|
|
492
|
+
return {
|
|
493
|
+
promise: () => __awaiter(this, void 0, void 0, function* () {
|
|
494
|
+
try {
|
|
495
|
+
return yield fn();
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
}),
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
getTable(tableName) {
|
|
504
|
+
const existing = this.tables.get(tableName);
|
|
505
|
+
if (existing)
|
|
506
|
+
return existing;
|
|
507
|
+
const next = new InMemoryTableState();
|
|
508
|
+
this.tables.set(tableName, next);
|
|
509
|
+
return next;
|
|
510
|
+
}
|
|
511
|
+
assertSupportedParams(method, params) {
|
|
512
|
+
var _a;
|
|
513
|
+
const spec = IN_MEMORY_SPEC.methods[method];
|
|
514
|
+
if (!spec) {
|
|
515
|
+
throw new NotSupportedError({
|
|
516
|
+
method,
|
|
517
|
+
featurePath: method,
|
|
518
|
+
reason: `Unsupported method: ${method}`,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
for (const [key, value] of Object.entries(params)) {
|
|
522
|
+
if (typeof value === "undefined")
|
|
523
|
+
continue;
|
|
524
|
+
if ((_a = spec.unsupportedParams) === null || _a === void 0 ? void 0 : _a.includes(key)) {
|
|
525
|
+
throw new NotSupportedError({
|
|
526
|
+
method,
|
|
527
|
+
featurePath: `${method}.${key}`,
|
|
528
|
+
reason: `${key} is not supported in in-memory mode.`,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
if (!spec.supportedParams.includes(key)) {
|
|
532
|
+
throw new NotSupportedError({
|
|
533
|
+
method,
|
|
534
|
+
featurePath: `${method}.${key}`,
|
|
535
|
+
reason: `Unsupported parameter: ${key}`,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
getRequiredTableName(params) {
|
|
541
|
+
if (typeof params.TableName !== "string" || !params.TableName) {
|
|
542
|
+
throw new NotSupportedError({
|
|
543
|
+
method: "documentClient",
|
|
544
|
+
featurePath: "TableName",
|
|
545
|
+
reason: "TableName must be a non-empty string.",
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
return params.TableName;
|
|
549
|
+
}
|
|
550
|
+
getRequiredKey(params, featurePath) {
|
|
551
|
+
const key = params.Key;
|
|
552
|
+
if (!key || typeof key !== "object") {
|
|
553
|
+
throw new NotSupportedError({
|
|
554
|
+
method: "documentClient",
|
|
555
|
+
featurePath,
|
|
556
|
+
reason: "Key must be an object.",
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (typeof key.PK !== "string" || typeof key.SK !== "string") {
|
|
560
|
+
throw this.validationError("One or more parameter values were invalid: Type mismatch for key");
|
|
561
|
+
}
|
|
562
|
+
return { PK: key.PK, SK: key.SK };
|
|
563
|
+
}
|
|
564
|
+
assertPrimaryKey(item, method) {
|
|
565
|
+
if (typeof item.PK !== "string" || typeof item.SK !== "string") {
|
|
566
|
+
throw this.validationError("One or more parameter values were invalid: Type mismatch for key");
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
assertCondition(method, params, item) {
|
|
570
|
+
if (!params.ConditionExpression)
|
|
571
|
+
return;
|
|
572
|
+
const ok = this.evaluateConditionOrValidationError(params.ConditionExpression, item, {
|
|
573
|
+
method,
|
|
574
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
575
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
576
|
+
}, "ConditionExpression");
|
|
577
|
+
if (!ok) {
|
|
578
|
+
throw this.awsError("ConditionalCheckFailedException", "The conditional request failed.");
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
resolveIndexName(indexName) {
|
|
582
|
+
if (!indexName)
|
|
583
|
+
return PRIMARY_INDEX_NAME;
|
|
584
|
+
if (indexName === "GSI1") {
|
|
585
|
+
throw new NotSupportedError({
|
|
586
|
+
method: "query",
|
|
587
|
+
featurePath: "query.IndexName",
|
|
588
|
+
reason: "GSI1 is intentionally excluded from in-memory mode.",
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
if (!isSupportedIndexName(indexName)) {
|
|
592
|
+
throw new NotSupportedError({
|
|
593
|
+
method: "query",
|
|
594
|
+
featurePath: "query.IndexName",
|
|
595
|
+
reason: `Unsupported index: ${indexName}`,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return parseIndexName(indexName);
|
|
599
|
+
}
|
|
600
|
+
resolveExclusiveStartKey(startKey, indexName, table) {
|
|
601
|
+
const key = this.getRequiredKey({ Key: startKey }, "query.ExclusiveStartKey");
|
|
602
|
+
const descriptor = table.getDescriptor(indexName);
|
|
603
|
+
const rangeKey = startKey === null || startKey === void 0 ? void 0 : startKey[descriptor.rangeAttribute];
|
|
604
|
+
const hashKey = startKey === null || startKey === void 0 ? void 0 : startKey[descriptor.hashAttribute];
|
|
605
|
+
if (typeof rangeKey !== "string" || typeof hashKey !== "string") {
|
|
606
|
+
throw this.validationError("Exclusive Start Key must have same size as table's key schema");
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
itemKey: encodeItemKey(key.PK, key.SK),
|
|
610
|
+
rangeKey,
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
buildLastEvaluatedKey(indexName, item) {
|
|
614
|
+
const key = {
|
|
615
|
+
PK: item.PK,
|
|
616
|
+
SK: item.SK,
|
|
617
|
+
};
|
|
618
|
+
if (indexName !== PRIMARY_INDEX_NAME) {
|
|
619
|
+
key[`${indexName}PK`] = item[`${indexName}PK`];
|
|
620
|
+
key[`${indexName}SK`] = item[`${indexName}SK`];
|
|
621
|
+
}
|
|
622
|
+
return key;
|
|
623
|
+
}
|
|
624
|
+
normalizeLimit(value, method) {
|
|
625
|
+
if (typeof value === "undefined")
|
|
626
|
+
return undefined;
|
|
627
|
+
if (typeof value !== "number" ||
|
|
628
|
+
!Number.isFinite(value) ||
|
|
629
|
+
value < 1) {
|
|
630
|
+
throw this.validationError("Limit must be greater than or equal to 1");
|
|
631
|
+
}
|
|
632
|
+
return Math.floor(value);
|
|
633
|
+
}
|
|
634
|
+
awsError(code, message) {
|
|
635
|
+
const error = new Error(message);
|
|
636
|
+
error.code = code;
|
|
637
|
+
error.name = code;
|
|
638
|
+
return error;
|
|
639
|
+
}
|
|
640
|
+
transactionCanceledError(message) {
|
|
641
|
+
return this.awsError("TransactionCanceledException", message);
|
|
642
|
+
}
|
|
643
|
+
validationError(message) {
|
|
644
|
+
return this.awsError("ValidationException", message);
|
|
645
|
+
}
|
|
646
|
+
parseKeyConditionOrValidationError(expression, context) {
|
|
647
|
+
try {
|
|
648
|
+
return parseKeyConditionExpression(expression, context);
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
if (error instanceof NotSupportedError) {
|
|
652
|
+
throw this.validationError(this.toDynamoExpressionValidationMessage("KeyConditionExpression", error));
|
|
653
|
+
}
|
|
654
|
+
throw error;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
parseUpdateExpressionOrValidationError(expression, context) {
|
|
658
|
+
try {
|
|
659
|
+
return parseUpdateExpression(expression, context);
|
|
660
|
+
}
|
|
661
|
+
catch (error) {
|
|
662
|
+
if (error instanceof NotSupportedError) {
|
|
663
|
+
throw this.validationError(this.toDynamoExpressionValidationMessage("UpdateExpression", error));
|
|
664
|
+
}
|
|
665
|
+
throw error;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
evaluateConditionOrValidationError(expression, item, context, expressionType) {
|
|
669
|
+
try {
|
|
670
|
+
return evaluateConditionExpression(expression, item, context);
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
if (error instanceof NotSupportedError) {
|
|
674
|
+
throw this.validationError(this.toDynamoExpressionValidationMessage(expressionType, error));
|
|
675
|
+
}
|
|
676
|
+
throw error;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
toDynamoExpressionValidationMessage(expressionType, error) {
|
|
680
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
681
|
+
const reason = String((_a = error.reason) !== null && _a !== void 0 ? _a : "").replace(/\.$/, "");
|
|
682
|
+
const namePlaceholder = (_b = error.featurePath.match(/ExpressionAttributeNames\.(#[A-Za-z_][A-Za-z0-9_]*)$/)) === null || _b === void 0 ? void 0 : _b[1];
|
|
683
|
+
if (namePlaceholder) {
|
|
684
|
+
return `Invalid ${expressionType}: An expression attribute name used in the document path is not defined; attribute name: ${namePlaceholder}`;
|
|
685
|
+
}
|
|
686
|
+
const valuePlaceholder = (_c = error.featurePath.match(/ExpressionAttributeValues\.(:[A-Za-z_][A-Za-z0-9_]*)$/)) === null || _c === void 0 ? void 0 : _c[1];
|
|
687
|
+
if (reason === "ExpressionAttributeValues are required for value placeholders" ||
|
|
688
|
+
reason === "Missing expression attribute value placeholder") {
|
|
689
|
+
const token = valuePlaceholder !== null && valuePlaceholder !== void 0 ? valuePlaceholder : ":missing";
|
|
690
|
+
return `Invalid ${expressionType}: An expression attribute value used in expression is not defined; attribute value: ${token}`;
|
|
691
|
+
}
|
|
692
|
+
if (reason === "Malformed SET assignment" ||
|
|
693
|
+
reason === "Malformed REMOVE assignment") {
|
|
694
|
+
const token = reason.includes("REMOVE") ? "REMOVE" : "SET";
|
|
695
|
+
return `Invalid UpdateExpression: Syntax error; token: "<EOF>", near: "${token}"`;
|
|
696
|
+
}
|
|
697
|
+
const invalidFunctionFromClause = (_d = reason.match(/^Unsupported clause:\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/)) === null || _d === void 0 ? void 0 : _d[1];
|
|
698
|
+
if (invalidFunctionFromClause) {
|
|
699
|
+
return `Invalid ${expressionType}: Invalid function name; function: ${invalidFunctionFromClause}`;
|
|
700
|
+
}
|
|
701
|
+
const unsupportedValue = (_e = reason.match(/^Unsupported value token:\s*(.+)$/)) === null || _e === void 0 ? void 0 : _e[1];
|
|
702
|
+
if (unsupportedValue) {
|
|
703
|
+
const fn = (_g = (_f = unsupportedValue.match(/\band\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/i)) === null || _f === void 0 ? void 0 : _f[1]) !== null && _g !== void 0 ? _g : (_h = unsupportedValue.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*\(/)) === null || _h === void 0 ? void 0 : _h[1];
|
|
704
|
+
if (fn) {
|
|
705
|
+
return `Invalid ${expressionType}: Invalid function name; function: ${fn}`;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return reason;
|
|
709
|
+
}
|
|
710
|
+
assertExpressionAttributeInputs(params, expressions) {
|
|
711
|
+
var _a, _b;
|
|
712
|
+
const activeExpressions = expressions.filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
713
|
+
if (activeExpressions.length === 0)
|
|
714
|
+
return;
|
|
715
|
+
if (typeof params.ExpressionAttributeValues !== "undefined") {
|
|
716
|
+
const values = params.ExpressionAttributeValues;
|
|
717
|
+
if (!values || typeof values !== "object" || Array.isArray(values)) {
|
|
718
|
+
throw this.validationError("ExpressionAttributeValues must not be empty");
|
|
719
|
+
}
|
|
720
|
+
const valueKeys = Object.keys(values);
|
|
721
|
+
if (valueKeys.length === 0) {
|
|
722
|
+
throw this.validationError("ExpressionAttributeValues must not be empty");
|
|
723
|
+
}
|
|
724
|
+
const usedValueTokens = new Set();
|
|
725
|
+
for (const expression of activeExpressions) {
|
|
726
|
+
for (const token of (_a = expression.match(/:[A-Za-z_][A-Za-z0-9_]*/g)) !== null && _a !== void 0 ? _a : []) {
|
|
727
|
+
usedValueTokens.add(token);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const unusedValues = valueKeys
|
|
731
|
+
.filter((token) => !usedValueTokens.has(token))
|
|
732
|
+
.sort();
|
|
733
|
+
if (unusedValues.length > 0) {
|
|
734
|
+
throw this.validationError(`Value provided in ExpressionAttributeValues unused in expressions: keys: {${unusedValues.join(", ")}}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (typeof params.ExpressionAttributeNames !== "undefined") {
|
|
738
|
+
const names = params.ExpressionAttributeNames;
|
|
739
|
+
if (!names || typeof names !== "object" || Array.isArray(names)) {
|
|
740
|
+
throw this.validationError("ExpressionAttributeNames must not be empty");
|
|
741
|
+
}
|
|
742
|
+
const nameKeys = Object.keys(names);
|
|
743
|
+
if (nameKeys.length === 0) {
|
|
744
|
+
throw this.validationError("ExpressionAttributeNames must not be empty");
|
|
745
|
+
}
|
|
746
|
+
const usedNameTokens = new Set();
|
|
747
|
+
for (const expression of activeExpressions) {
|
|
748
|
+
for (const token of (_b = expression.match(/#[A-Za-z_][A-Za-z0-9_]*/g)) !== null && _b !== void 0 ? _b : []) {
|
|
749
|
+
usedNameTokens.add(token);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const unusedNames = nameKeys
|
|
753
|
+
.filter((token) => !usedNameTokens.has(token))
|
|
754
|
+
.sort();
|
|
755
|
+
if (unusedNames.length > 0) {
|
|
756
|
+
throw this.validationError(`Value provided in ExpressionAttributeNames unused in expressions: keys: {${unusedNames.join(", ")}}`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const createUnsupportedRequest = (method) => {
|
|
762
|
+
return (_params) => ({
|
|
763
|
+
promise: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
764
|
+
throw new NotSupportedError({
|
|
765
|
+
method,
|
|
766
|
+
featurePath: method,
|
|
767
|
+
reason: `${method} is not supported in in-memory mode.`,
|
|
768
|
+
});
|
|
769
|
+
}),
|
|
770
|
+
});
|
|
771
|
+
};
|
|
772
|
+
export const createInMemoryDocumentClient = () => {
|
|
773
|
+
const instance = new InMemoryDocumentClientImpl();
|
|
774
|
+
const proxied = new Proxy(instance, {
|
|
775
|
+
get(target, prop, receiver) {
|
|
776
|
+
if (typeof prop === "string") {
|
|
777
|
+
if (prop in target) {
|
|
778
|
+
const value = Reflect.get(target, prop, receiver);
|
|
779
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
780
|
+
}
|
|
781
|
+
if (IN_MEMORY_SPEC.unsupportedMethods.includes(prop)) {
|
|
782
|
+
return createUnsupportedRequest(prop);
|
|
783
|
+
}
|
|
784
|
+
return createUnsupportedRequest(prop);
|
|
785
|
+
}
|
|
786
|
+
return Reflect.get(target, prop, receiver);
|
|
787
|
+
},
|
|
788
|
+
});
|
|
789
|
+
return proxied;
|
|
790
|
+
};
|
|
791
|
+
//# sourceMappingURL=document-client.js.map
|