@celerity-sdk/datastore 0.3.1 → 0.4.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/README.md +5 -5
- package/dist/index.cjs +657 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +381 -1
- package/dist/index.d.ts +381 -1
- package/dist/index.js +631 -0
- package/dist/index.js.map +1 -1
- package/package.json +18 -7
package/dist/index.js
CHANGED
|
@@ -1 +1,632 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/types.ts
|
|
5
|
+
var DatastoreClient = /* @__PURE__ */ Symbol.for("DatastoreClient");
|
|
6
|
+
|
|
7
|
+
// src/providers/dynamodb/dynamodb-datastore-client.ts
|
|
8
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
9
|
+
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
|
|
10
|
+
|
|
11
|
+
// src/providers/dynamodb/dynamodb-datastore.ts
|
|
12
|
+
import createDebug2 from "debug";
|
|
13
|
+
import { GetCommand, PutCommand, DeleteCommand, BatchGetCommand, BatchWriteCommand } from "@aws-sdk/lib-dynamodb";
|
|
14
|
+
|
|
15
|
+
// src/errors.ts
|
|
16
|
+
var DatastoreError = class extends Error {
|
|
17
|
+
static {
|
|
18
|
+
__name(this, "DatastoreError");
|
|
19
|
+
}
|
|
20
|
+
table;
|
|
21
|
+
constructor(message, table, options) {
|
|
22
|
+
super(message, options), this.table = table;
|
|
23
|
+
this.name = "DatastoreError";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var ConditionalCheckFailedError = class extends DatastoreError {
|
|
27
|
+
static {
|
|
28
|
+
__name(this, "ConditionalCheckFailedError");
|
|
29
|
+
}
|
|
30
|
+
constructor(table, options) {
|
|
31
|
+
super(`Conditional check failed on table "${table}"`, table, options);
|
|
32
|
+
this.name = "ConditionalCheckFailedError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/providers/dynamodb/dynamodb-item-listing.ts
|
|
37
|
+
import createDebug from "debug";
|
|
38
|
+
import { QueryCommand, ScanCommand } from "@aws-sdk/lib-dynamodb";
|
|
39
|
+
|
|
40
|
+
// src/providers/dynamodb/expressions.ts
|
|
41
|
+
var COMPARISON_OPERATORS = {
|
|
42
|
+
eq: "=",
|
|
43
|
+
ne: "<>",
|
|
44
|
+
lt: "<",
|
|
45
|
+
le: "<=",
|
|
46
|
+
gt: ">",
|
|
47
|
+
ge: ">="
|
|
48
|
+
};
|
|
49
|
+
function buildKeyConditionExpression(key, range) {
|
|
50
|
+
const names = {};
|
|
51
|
+
const values = {};
|
|
52
|
+
let counter = 0;
|
|
53
|
+
const pkName = `#k${counter}`;
|
|
54
|
+
const pkValue = `:k${counter}`;
|
|
55
|
+
names[pkName] = key.name;
|
|
56
|
+
values[pkValue] = key.value;
|
|
57
|
+
counter++;
|
|
58
|
+
let expression = `${pkName} = ${pkValue}`;
|
|
59
|
+
if (range) {
|
|
60
|
+
const skName = `#k${counter}`;
|
|
61
|
+
names[skName] = range.name;
|
|
62
|
+
switch (range.operator) {
|
|
63
|
+
case "eq":
|
|
64
|
+
case "lt":
|
|
65
|
+
case "le":
|
|
66
|
+
case "gt":
|
|
67
|
+
case "ge": {
|
|
68
|
+
const skValue = `:k${counter}`;
|
|
69
|
+
values[skValue] = range.value;
|
|
70
|
+
expression += ` AND ${skName} ${COMPARISON_OPERATORS[range.operator]} ${skValue}`;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "between": {
|
|
74
|
+
const lowVal = `:k${counter}a`;
|
|
75
|
+
const highVal = `:k${counter}b`;
|
|
76
|
+
values[lowVal] = range.low;
|
|
77
|
+
values[highVal] = range.high;
|
|
78
|
+
expression += ` AND ${skName} BETWEEN ${lowVal} AND ${highVal}`;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "startsWith": {
|
|
82
|
+
const skValue = `:k${counter}`;
|
|
83
|
+
values[skValue] = range.value;
|
|
84
|
+
expression += ` AND begins_with(${skName}, ${skValue})`;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
expression,
|
|
91
|
+
names,
|
|
92
|
+
values
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
__name(buildKeyConditionExpression, "buildKeyConditionExpression");
|
|
96
|
+
function buildFilterExpression(conditions) {
|
|
97
|
+
const condArray = Array.isArray(conditions) ? conditions : [
|
|
98
|
+
conditions
|
|
99
|
+
];
|
|
100
|
+
const names = {};
|
|
101
|
+
const values = {};
|
|
102
|
+
const parts = [];
|
|
103
|
+
let counter = 0;
|
|
104
|
+
for (const cond of condArray) {
|
|
105
|
+
const attrName = `#f${counter}`;
|
|
106
|
+
names[attrName] = cond.name;
|
|
107
|
+
switch (cond.operator) {
|
|
108
|
+
case "eq":
|
|
109
|
+
case "ne":
|
|
110
|
+
case "lt":
|
|
111
|
+
case "le":
|
|
112
|
+
case "gt":
|
|
113
|
+
case "ge": {
|
|
114
|
+
const valKey = `:f${counter}`;
|
|
115
|
+
values[valKey] = cond.value;
|
|
116
|
+
parts.push(`${attrName} ${COMPARISON_OPERATORS[cond.operator]} ${valKey}`);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case "between": {
|
|
120
|
+
const lowVal = `:f${counter}a`;
|
|
121
|
+
const highVal = `:f${counter}b`;
|
|
122
|
+
values[lowVal] = cond.low;
|
|
123
|
+
values[highVal] = cond.high;
|
|
124
|
+
parts.push(`${attrName} BETWEEN ${lowVal} AND ${highVal}`);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "startsWith":
|
|
128
|
+
case "contains": {
|
|
129
|
+
const valKey = `:f${counter}`;
|
|
130
|
+
values[valKey] = cond.value;
|
|
131
|
+
const fnName = cond.operator === "startsWith" ? "begins_with" : "contains";
|
|
132
|
+
parts.push(`${fnName}(${attrName}, ${valKey})`);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "exists":
|
|
136
|
+
parts.push(`attribute_exists(${attrName})`);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
counter++;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
expression: parts.join(" AND "),
|
|
143
|
+
names,
|
|
144
|
+
values
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
__name(buildFilterExpression, "buildFilterExpression");
|
|
148
|
+
|
|
149
|
+
// src/providers/dynamodb/dynamodb-item-listing.ts
|
|
150
|
+
var debug = createDebug("celerity:datastore:dynamodb");
|
|
151
|
+
function encodeCursor(lastEvaluatedKey) {
|
|
152
|
+
const state = {
|
|
153
|
+
lastEvaluatedKey
|
|
154
|
+
};
|
|
155
|
+
return Buffer.from(JSON.stringify(state)).toString("base64url");
|
|
156
|
+
}
|
|
157
|
+
__name(encodeCursor, "encodeCursor");
|
|
158
|
+
function decodeCursor(cursor) {
|
|
159
|
+
return JSON.parse(Buffer.from(cursor, "base64url").toString("utf-8"));
|
|
160
|
+
}
|
|
161
|
+
__name(decodeCursor, "decodeCursor");
|
|
162
|
+
var DynamoDBItemListing = class {
|
|
163
|
+
static {
|
|
164
|
+
__name(this, "DynamoDBItemListing");
|
|
165
|
+
}
|
|
166
|
+
client;
|
|
167
|
+
tableName;
|
|
168
|
+
mode;
|
|
169
|
+
options;
|
|
170
|
+
tracer;
|
|
171
|
+
_cursor;
|
|
172
|
+
constructor(client, tableName, mode, options, tracer) {
|
|
173
|
+
this.client = client;
|
|
174
|
+
this.tableName = tableName;
|
|
175
|
+
this.mode = mode;
|
|
176
|
+
this.options = options;
|
|
177
|
+
this.tracer = tracer;
|
|
178
|
+
this._cursor = options.cursor;
|
|
179
|
+
}
|
|
180
|
+
get cursor() {
|
|
181
|
+
return this._cursor;
|
|
182
|
+
}
|
|
183
|
+
async *[Symbol.asyncIterator]() {
|
|
184
|
+
const cursorState = this._cursor ? decodeCursor(this._cursor) : void 0;
|
|
185
|
+
let exclusiveStartKey = cursorState?.lastEvaluatedKey;
|
|
186
|
+
do {
|
|
187
|
+
debug("%s page %s key=%o", this.mode, this.tableName, exclusiveStartKey ?? "(start)");
|
|
188
|
+
const response = await this.fetchPage(exclusiveStartKey);
|
|
189
|
+
for (const item of response.Items ?? []) {
|
|
190
|
+
yield item;
|
|
191
|
+
}
|
|
192
|
+
exclusiveStartKey = response.LastEvaluatedKey;
|
|
193
|
+
if (exclusiveStartKey) {
|
|
194
|
+
this._cursor = encodeCursor(exclusiveStartKey);
|
|
195
|
+
} else {
|
|
196
|
+
this._cursor = void 0;
|
|
197
|
+
}
|
|
198
|
+
} while (exclusiveStartKey);
|
|
199
|
+
}
|
|
200
|
+
async fetchPage(exclusiveStartKey) {
|
|
201
|
+
const command = this.mode === "query" ? this.buildQueryCommand(exclusiveStartKey) : this.buildScanCommand(exclusiveStartKey);
|
|
202
|
+
const doFetch = /* @__PURE__ */ __name(async () => {
|
|
203
|
+
try {
|
|
204
|
+
return await this.client.send(command);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
throw new DatastoreError(`Failed to ${this.mode} table "${this.tableName}"`, this.tableName, {
|
|
207
|
+
cause: error
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}, "doFetch");
|
|
211
|
+
if (!this.tracer) return doFetch();
|
|
212
|
+
return this.tracer.withSpan(`celerity.datastore.${this.mode}_page`, () => doFetch(), {
|
|
213
|
+
"datastore.table": this.tableName
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
buildQueryCommand(exclusiveStartKey) {
|
|
217
|
+
const opts = this.options;
|
|
218
|
+
const keyExpr = buildKeyConditionExpression(opts.key, opts.range);
|
|
219
|
+
const filterExpr = opts.filter ? buildFilterExpression(opts.filter) : void 0;
|
|
220
|
+
return new QueryCommand({
|
|
221
|
+
TableName: this.tableName,
|
|
222
|
+
IndexName: opts.indexName,
|
|
223
|
+
KeyConditionExpression: keyExpr.expression,
|
|
224
|
+
FilterExpression: filterExpr?.expression,
|
|
225
|
+
ExpressionAttributeNames: {
|
|
226
|
+
...keyExpr.names,
|
|
227
|
+
...filterExpr?.names
|
|
228
|
+
},
|
|
229
|
+
ExpressionAttributeValues: {
|
|
230
|
+
...keyExpr.values,
|
|
231
|
+
...filterExpr?.values
|
|
232
|
+
},
|
|
233
|
+
ScanIndexForward: opts.sortAscending,
|
|
234
|
+
Limit: opts.maxResults,
|
|
235
|
+
ExclusiveStartKey: exclusiveStartKey,
|
|
236
|
+
ConsistentRead: opts.consistentRead
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
buildScanCommand(exclusiveStartKey) {
|
|
240
|
+
const opts = this.options;
|
|
241
|
+
const filterExpr = opts.filter ? buildFilterExpression(opts.filter) : void 0;
|
|
242
|
+
return new ScanCommand({
|
|
243
|
+
TableName: this.tableName,
|
|
244
|
+
IndexName: opts.indexName,
|
|
245
|
+
FilterExpression: filterExpr?.expression,
|
|
246
|
+
ExpressionAttributeNames: filterExpr?.names && Object.keys(filterExpr.names).length > 0 ? filterExpr.names : void 0,
|
|
247
|
+
ExpressionAttributeValues: filterExpr?.values && Object.keys(filterExpr.values).length > 0 ? filterExpr.values : void 0,
|
|
248
|
+
Limit: opts.maxResults,
|
|
249
|
+
ExclusiveStartKey: exclusiveStartKey,
|
|
250
|
+
ConsistentRead: opts.consistentRead
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/providers/dynamodb/errors.ts
|
|
256
|
+
function isConditionalCheckFailedError(error) {
|
|
257
|
+
if (!(error instanceof Error)) return false;
|
|
258
|
+
return error.name === "ConditionalCheckFailedException";
|
|
259
|
+
}
|
|
260
|
+
__name(isConditionalCheckFailedError, "isConditionalCheckFailedError");
|
|
261
|
+
|
|
262
|
+
// src/providers/dynamodb/dynamodb-datastore.ts
|
|
263
|
+
var debug2 = createDebug2("celerity:datastore:dynamodb");
|
|
264
|
+
var DynamoDBDatastore = class {
|
|
265
|
+
static {
|
|
266
|
+
__name(this, "DynamoDBDatastore");
|
|
267
|
+
}
|
|
268
|
+
tableName;
|
|
269
|
+
client;
|
|
270
|
+
tracer;
|
|
271
|
+
constructor(tableName, client, tracer) {
|
|
272
|
+
this.tableName = tableName;
|
|
273
|
+
this.client = client;
|
|
274
|
+
this.tracer = tracer;
|
|
275
|
+
}
|
|
276
|
+
async getItem(key, options) {
|
|
277
|
+
debug2("getItem %s %o", this.tableName, key);
|
|
278
|
+
return this.traced("celerity.datastore.get_item", {
|
|
279
|
+
"datastore.table": this.tableName
|
|
280
|
+
}, async () => {
|
|
281
|
+
try {
|
|
282
|
+
const response = await this.client.send(new GetCommand({
|
|
283
|
+
TableName: this.tableName,
|
|
284
|
+
Key: key,
|
|
285
|
+
ConsistentRead: options?.consistentRead
|
|
286
|
+
}));
|
|
287
|
+
return response.Item ?? null;
|
|
288
|
+
} catch (error) {
|
|
289
|
+
throw new DatastoreError(`Failed to get item from table "${this.tableName}"`, this.tableName, {
|
|
290
|
+
cause: error
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
async putItem(item, options) {
|
|
296
|
+
debug2("putItem %s", this.tableName);
|
|
297
|
+
return this.traced("celerity.datastore.put_item", {
|
|
298
|
+
"datastore.table": this.tableName
|
|
299
|
+
}, async () => {
|
|
300
|
+
try {
|
|
301
|
+
const conditionParams = options?.condition ? buildFilterExpression(options.condition) : void 0;
|
|
302
|
+
await this.client.send(new PutCommand({
|
|
303
|
+
TableName: this.tableName,
|
|
304
|
+
Item: item,
|
|
305
|
+
ConditionExpression: conditionParams?.expression,
|
|
306
|
+
ExpressionAttributeNames: conditionParams?.names,
|
|
307
|
+
ExpressionAttributeValues: conditionParams?.values
|
|
308
|
+
}));
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (isConditionalCheckFailedError(error)) {
|
|
311
|
+
throw new ConditionalCheckFailedError(this.tableName, {
|
|
312
|
+
cause: error
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
throw new DatastoreError(`Failed to put item in table "${this.tableName}"`, this.tableName, {
|
|
316
|
+
cause: error
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async deleteItem(key, options) {
|
|
322
|
+
debug2("deleteItem %s %o", this.tableName, key);
|
|
323
|
+
return this.traced("celerity.datastore.delete_item", {
|
|
324
|
+
"datastore.table": this.tableName
|
|
325
|
+
}, async () => {
|
|
326
|
+
try {
|
|
327
|
+
const conditionParams = options?.condition ? buildFilterExpression(options.condition) : void 0;
|
|
328
|
+
await this.client.send(new DeleteCommand({
|
|
329
|
+
TableName: this.tableName,
|
|
330
|
+
Key: key,
|
|
331
|
+
ConditionExpression: conditionParams?.expression,
|
|
332
|
+
ExpressionAttributeNames: conditionParams?.names,
|
|
333
|
+
ExpressionAttributeValues: conditionParams?.values
|
|
334
|
+
}));
|
|
335
|
+
} catch (error) {
|
|
336
|
+
if (isConditionalCheckFailedError(error)) {
|
|
337
|
+
throw new ConditionalCheckFailedError(this.tableName, {
|
|
338
|
+
cause: error
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
throw new DatastoreError(`Failed to delete item from table "${this.tableName}"`, this.tableName, {
|
|
342
|
+
cause: error
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
query(options) {
|
|
348
|
+
debug2("query %s pk=%s", this.tableName, options.key.name);
|
|
349
|
+
return new DynamoDBItemListing(this.client, this.tableName, "query", options, this.tracer);
|
|
350
|
+
}
|
|
351
|
+
scan(options) {
|
|
352
|
+
debug2("scan %s", this.tableName);
|
|
353
|
+
return new DynamoDBItemListing(this.client, this.tableName, "scan", options ?? {}, this.tracer);
|
|
354
|
+
}
|
|
355
|
+
async batchGetItems(keys, options) {
|
|
356
|
+
debug2("batchGetItems %s count=%d", this.tableName, keys.length);
|
|
357
|
+
return this.traced("celerity.datastore.batch_get_items", {
|
|
358
|
+
"datastore.table": this.tableName,
|
|
359
|
+
"datastore.batch_size": keys.length
|
|
360
|
+
}, async () => {
|
|
361
|
+
try {
|
|
362
|
+
const response = await this.client.send(new BatchGetCommand({
|
|
363
|
+
RequestItems: {
|
|
364
|
+
[this.tableName]: {
|
|
365
|
+
Keys: keys,
|
|
366
|
+
ConsistentRead: options?.consistentRead
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}));
|
|
370
|
+
const items = response.Responses?.[this.tableName] ?? [];
|
|
371
|
+
const unprocessedKeys = response.UnprocessedKeys?.[this.tableName]?.Keys ?? [];
|
|
372
|
+
return {
|
|
373
|
+
items,
|
|
374
|
+
unprocessedKeys
|
|
375
|
+
};
|
|
376
|
+
} catch (error) {
|
|
377
|
+
throw new DatastoreError(`Failed to batch get items from table "${this.tableName}"`, this.tableName, {
|
|
378
|
+
cause: error
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
async batchWriteItems(operations) {
|
|
384
|
+
debug2("batchWriteItems %s count=%d", this.tableName, operations.length);
|
|
385
|
+
return this.traced("celerity.datastore.batch_write_items", {
|
|
386
|
+
"datastore.table": this.tableName,
|
|
387
|
+
"datastore.batch_size": operations.length
|
|
388
|
+
}, async () => {
|
|
389
|
+
try {
|
|
390
|
+
const writeRequests = operations.map((op) => {
|
|
391
|
+
if (op.type === "put") {
|
|
392
|
+
return {
|
|
393
|
+
PutRequest: {
|
|
394
|
+
Item: op.item
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
DeleteRequest: {
|
|
400
|
+
Key: op.key
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
const response = await this.client.send(new BatchWriteCommand({
|
|
405
|
+
RequestItems: {
|
|
406
|
+
[this.tableName]: writeRequests
|
|
407
|
+
}
|
|
408
|
+
}));
|
|
409
|
+
const unprocessedRequests = response.UnprocessedItems?.[this.tableName] ?? [];
|
|
410
|
+
const unprocessedOperations = unprocessedRequests.map((req) => {
|
|
411
|
+
if (req.PutRequest) {
|
|
412
|
+
return {
|
|
413
|
+
type: "put",
|
|
414
|
+
item: req.PutRequest.Item
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
type: "delete",
|
|
419
|
+
key: req.DeleteRequest.Key
|
|
420
|
+
};
|
|
421
|
+
});
|
|
422
|
+
return {
|
|
423
|
+
unprocessedOperations
|
|
424
|
+
};
|
|
425
|
+
} catch (error) {
|
|
426
|
+
throw new DatastoreError(`Failed to batch write items to table "${this.tableName}"`, this.tableName, {
|
|
427
|
+
cause: error
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
traced(name, attributes, fn) {
|
|
433
|
+
if (!this.tracer) return fn();
|
|
434
|
+
return this.tracer.withSpan(name, (span) => fn(span), attributes);
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// src/providers/dynamodb/config.ts
|
|
439
|
+
function captureDynamoDBConfig() {
|
|
440
|
+
return {
|
|
441
|
+
region: process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION,
|
|
442
|
+
endpoint: process.env.AWS_ENDPOINT_URL,
|
|
443
|
+
credentials: process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY ? {
|
|
444
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
445
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
446
|
+
} : void 0
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
__name(captureDynamoDBConfig, "captureDynamoDBConfig");
|
|
450
|
+
|
|
451
|
+
// src/providers/dynamodb/dynamodb-datastore-client.ts
|
|
452
|
+
var DynamoDBDatastoreClient = class {
|
|
453
|
+
static {
|
|
454
|
+
__name(this, "DynamoDBDatastoreClient");
|
|
455
|
+
}
|
|
456
|
+
tracer;
|
|
457
|
+
client = null;
|
|
458
|
+
docClient = null;
|
|
459
|
+
config;
|
|
460
|
+
constructor(config, tracer) {
|
|
461
|
+
this.tracer = tracer;
|
|
462
|
+
this.config = config ?? captureDynamoDBConfig();
|
|
463
|
+
}
|
|
464
|
+
datastore(name) {
|
|
465
|
+
return new DynamoDBDatastore(name, this.getDocClient(), this.tracer);
|
|
466
|
+
}
|
|
467
|
+
close() {
|
|
468
|
+
this.docClient?.destroy();
|
|
469
|
+
this.client?.destroy();
|
|
470
|
+
this.docClient = null;
|
|
471
|
+
this.client = null;
|
|
472
|
+
}
|
|
473
|
+
getDocClient() {
|
|
474
|
+
if (!this.docClient) {
|
|
475
|
+
this.client = new DynamoDBClient({
|
|
476
|
+
region: this.config.region,
|
|
477
|
+
endpoint: this.config.endpoint,
|
|
478
|
+
credentials: this.config.credentials
|
|
479
|
+
});
|
|
480
|
+
this.docClient = DynamoDBDocumentClient.from(this.client, {
|
|
481
|
+
marshallOptions: {
|
|
482
|
+
removeUndefinedValues: true
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
return this.docClient;
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// src/factory.ts
|
|
491
|
+
import { resolveConfig } from "@celerity-sdk/config";
|
|
492
|
+
function createDatastoreClient(options) {
|
|
493
|
+
const resolved = resolveConfig("datastore");
|
|
494
|
+
const provider = options?.provider ?? resolved.provider;
|
|
495
|
+
switch (provider) {
|
|
496
|
+
case "aws":
|
|
497
|
+
return new DynamoDBDatastoreClient(options?.aws, options?.tracer);
|
|
498
|
+
case "local":
|
|
499
|
+
return createLocalClient(options);
|
|
500
|
+
default:
|
|
501
|
+
throw new Error(`Unsupported datastore provider: "${provider}"`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
__name(createDatastoreClient, "createDatastoreClient");
|
|
505
|
+
function createLocalClient(options) {
|
|
506
|
+
const deployTarget = options?.deployTarget?.toLowerCase();
|
|
507
|
+
switch (deployTarget) {
|
|
508
|
+
case "aws":
|
|
509
|
+
case "aws-serverless":
|
|
510
|
+
case void 0: {
|
|
511
|
+
const localConfig = {
|
|
512
|
+
...captureDynamoDBConfig(),
|
|
513
|
+
...options?.aws
|
|
514
|
+
};
|
|
515
|
+
return new DynamoDBDatastoreClient(localConfig, options?.tracer);
|
|
516
|
+
}
|
|
517
|
+
// case "gcloud":
|
|
518
|
+
// case "gcloud-serverless":
|
|
519
|
+
// v1: Firestore emulator
|
|
520
|
+
// case "azure":
|
|
521
|
+
// case "azure-serverless":
|
|
522
|
+
// v1: Cosmos DB emulator
|
|
523
|
+
default:
|
|
524
|
+
throw new Error(`Unsupported local datastore deploy target: "${deployTarget}". Only AWS is supported in v0.`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
__name(createLocalClient, "createLocalClient");
|
|
528
|
+
|
|
529
|
+
// src/decorators.ts
|
|
530
|
+
import "reflect-metadata";
|
|
531
|
+
import { INJECT_METADATA, USE_RESOURCE_METADATA } from "@celerity-sdk/common";
|
|
532
|
+
function datastoreToken(resourceName) {
|
|
533
|
+
return /* @__PURE__ */ Symbol.for(`celerity:datastore:${resourceName}`);
|
|
534
|
+
}
|
|
535
|
+
__name(datastoreToken, "datastoreToken");
|
|
536
|
+
var DEFAULT_DATASTORE_TOKEN = /* @__PURE__ */ Symbol.for("celerity:datastore:default");
|
|
537
|
+
function Datastore(resourceName) {
|
|
538
|
+
return (target, _propertyKey, parameterIndex) => {
|
|
539
|
+
const token = resourceName ? datastoreToken(resourceName) : DEFAULT_DATASTORE_TOKEN;
|
|
540
|
+
const existing = Reflect.getOwnMetadata(INJECT_METADATA, target) ?? /* @__PURE__ */ new Map();
|
|
541
|
+
existing.set(parameterIndex, token);
|
|
542
|
+
Reflect.defineMetadata(INJECT_METADATA, existing, target);
|
|
543
|
+
if (resourceName) {
|
|
544
|
+
const resources = Reflect.getOwnMetadata(USE_RESOURCE_METADATA, target) ?? [];
|
|
545
|
+
if (!resources.includes(resourceName)) {
|
|
546
|
+
Reflect.defineMetadata(USE_RESOURCE_METADATA, [
|
|
547
|
+
...resources,
|
|
548
|
+
resourceName
|
|
549
|
+
], target);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
__name(Datastore, "Datastore");
|
|
555
|
+
|
|
556
|
+
// src/helpers.ts
|
|
557
|
+
function getDatastore(container, resourceName) {
|
|
558
|
+
const token = resourceName ? datastoreToken(resourceName) : DEFAULT_DATASTORE_TOKEN;
|
|
559
|
+
return container.resolve(token);
|
|
560
|
+
}
|
|
561
|
+
__name(getDatastore, "getDatastore");
|
|
562
|
+
|
|
563
|
+
// src/layer.ts
|
|
564
|
+
import createDebug3 from "debug";
|
|
565
|
+
import { TRACER_TOKEN, CONFIG_SERVICE_TOKEN } from "@celerity-sdk/common";
|
|
566
|
+
import { captureResourceLinks, getLinksOfType, RESOURCE_CONFIG_NAMESPACE } from "@celerity-sdk/config";
|
|
567
|
+
var debug3 = createDebug3("celerity:datastore");
|
|
568
|
+
function captureDatastoreLayerConfig() {
|
|
569
|
+
return {
|
|
570
|
+
deployTarget: process.env.CELERITY_DEPLOY_TARGET
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
__name(captureDatastoreLayerConfig, "captureDatastoreLayerConfig");
|
|
574
|
+
var DatastoreLayer = class {
|
|
575
|
+
static {
|
|
576
|
+
__name(this, "DatastoreLayer");
|
|
577
|
+
}
|
|
578
|
+
initialized = false;
|
|
579
|
+
config = null;
|
|
580
|
+
async handle(context, next) {
|
|
581
|
+
if (!this.initialized) {
|
|
582
|
+
this.config = captureDatastoreLayerConfig();
|
|
583
|
+
const tracer = context.container.has(TRACER_TOKEN) ? await context.container.resolve(TRACER_TOKEN) : void 0;
|
|
584
|
+
const client = createDatastoreClient({
|
|
585
|
+
tracer,
|
|
586
|
+
deployTarget: this.config.deployTarget
|
|
587
|
+
});
|
|
588
|
+
debug3("registering DatastoreClient");
|
|
589
|
+
context.container.register("DatastoreClient", {
|
|
590
|
+
useValue: client
|
|
591
|
+
});
|
|
592
|
+
const links = captureResourceLinks();
|
|
593
|
+
const datastoreLinks = getLinksOfType(links, "datastore");
|
|
594
|
+
if (datastoreLinks.size > 0) {
|
|
595
|
+
const configService = await context.container.resolve(CONFIG_SERVICE_TOKEN);
|
|
596
|
+
const resourceConfig = configService.namespace(RESOURCE_CONFIG_NAMESPACE);
|
|
597
|
+
for (const [resourceName, configKey] of datastoreLinks) {
|
|
598
|
+
const actualName = await resourceConfig.getOrThrow(configKey);
|
|
599
|
+
debug3("registered datastore resource %s \u2192 %s", resourceName, actualName);
|
|
600
|
+
context.container.register(datastoreToken(resourceName), {
|
|
601
|
+
useValue: client.datastore(actualName)
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
if (datastoreLinks.size === 1) {
|
|
605
|
+
const [, configKey] = [
|
|
606
|
+
...datastoreLinks.entries()
|
|
607
|
+
][0];
|
|
608
|
+
const actualName = await resourceConfig.getOrThrow(configKey);
|
|
609
|
+
debug3("registered default datastore \u2192 %s", actualName);
|
|
610
|
+
context.container.register(DEFAULT_DATASTORE_TOKEN, {
|
|
611
|
+
useValue: client.datastore(actualName)
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
this.initialized = true;
|
|
616
|
+
}
|
|
617
|
+
return next();
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
export {
|
|
621
|
+
ConditionalCheckFailedError,
|
|
622
|
+
DEFAULT_DATASTORE_TOKEN,
|
|
623
|
+
Datastore,
|
|
624
|
+
DatastoreClient,
|
|
625
|
+
DatastoreError,
|
|
626
|
+
DatastoreLayer,
|
|
627
|
+
DynamoDBDatastoreClient,
|
|
628
|
+
createDatastoreClient,
|
|
629
|
+
datastoreToken,
|
|
630
|
+
getDatastore
|
|
631
|
+
};
|
|
1
632
|
//# sourceMappingURL=index.js.map
|