@celerity-sdk/datastore 0.4.0 → 0.5.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/dist/index.js CHANGED
@@ -1,500 +1,24 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
1
+ import {
2
+ ConditionalCheckFailedError,
3
+ DatastoreError
4
+ } from "./chunk-HHZJK2AK.js";
5
+ import {
6
+ __name
7
+ } from "./chunk-7QVYU63E.js";
3
8
 
4
9
  // src/types.ts
5
10
  var DatastoreClient = /* @__PURE__ */ Symbol.for("DatastoreClient");
6
11
 
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
12
  // src/factory.ts
491
13
  import { resolveConfig } from "@celerity-sdk/config";
492
- function createDatastoreClient(options) {
14
+ async function createDatastoreClient(options) {
493
15
  const resolved = resolveConfig("datastore");
494
16
  const provider = options?.provider ?? resolved.provider;
495
17
  switch (provider) {
496
- case "aws":
18
+ case "aws": {
19
+ const { DynamoDBDatastoreClient } = await import("./dynamodb-datastore-client-LQH3GS6X.js");
497
20
  return new DynamoDBDatastoreClient(options?.aws, options?.tracer);
21
+ }
498
22
  case "local":
499
23
  return createLocalClient(options);
500
24
  default:
@@ -502,12 +26,14 @@ function createDatastoreClient(options) {
502
26
  }
503
27
  }
504
28
  __name(createDatastoreClient, "createDatastoreClient");
505
- function createLocalClient(options) {
29
+ async function createLocalClient(options) {
506
30
  const deployTarget = options?.deployTarget?.toLowerCase();
507
31
  switch (deployTarget) {
508
32
  case "aws":
509
33
  case "aws-serverless":
510
34
  case void 0: {
35
+ const { captureDynamoDBConfig } = await import("./config-IXTAYSFF.js");
36
+ const { DynamoDBDatastoreClient } = await import("./dynamodb-datastore-client-LQH3GS6X.js");
511
37
  const localConfig = {
512
38
  ...captureDynamoDBConfig(),
513
39
  ...options?.aws
@@ -561,10 +87,10 @@ function getDatastore(container, resourceName) {
561
87
  __name(getDatastore, "getDatastore");
562
88
 
563
89
  // src/layer.ts
564
- import createDebug3 from "debug";
90
+ import createDebug from "debug";
565
91
  import { TRACER_TOKEN, CONFIG_SERVICE_TOKEN } from "@celerity-sdk/common";
566
92
  import { captureResourceLinks, getLinksOfType, RESOURCE_CONFIG_NAMESPACE } from "@celerity-sdk/config";
567
- var debug3 = createDebug3("celerity:datastore");
93
+ var debug = createDebug("celerity:datastore");
568
94
  function captureDatastoreLayerConfig() {
569
95
  return {
570
96
  deployTarget: process.env.CELERITY_DEPLOY_TARGET
@@ -581,11 +107,11 @@ var DatastoreLayer = class {
581
107
  if (!this.initialized) {
582
108
  this.config = captureDatastoreLayerConfig();
583
109
  const tracer = context.container.has(TRACER_TOKEN) ? await context.container.resolve(TRACER_TOKEN) : void 0;
584
- const client = createDatastoreClient({
110
+ const client = await createDatastoreClient({
585
111
  tracer,
586
112
  deployTarget: this.config.deployTarget
587
113
  });
588
- debug3("registering DatastoreClient");
114
+ debug("registering DatastoreClient");
589
115
  context.container.register("DatastoreClient", {
590
116
  useValue: client
591
117
  });
@@ -596,7 +122,7 @@ var DatastoreLayer = class {
596
122
  const resourceConfig = configService.namespace(RESOURCE_CONFIG_NAMESPACE);
597
123
  for (const [resourceName, configKey] of datastoreLinks) {
598
124
  const actualName = await resourceConfig.getOrThrow(configKey);
599
- debug3("registered datastore resource %s \u2192 %s", resourceName, actualName);
125
+ debug("registered datastore resource %s \u2192 %s", resourceName, actualName);
600
126
  context.container.register(datastoreToken(resourceName), {
601
127
  useValue: client.datastore(actualName)
602
128
  });
@@ -606,7 +132,7 @@ var DatastoreLayer = class {
606
132
  ...datastoreLinks.entries()
607
133
  ][0];
608
134
  const actualName = await resourceConfig.getOrThrow(configKey);
609
- debug3("registered default datastore \u2192 %s", actualName);
135
+ debug("registered default datastore \u2192 %s", actualName);
610
136
  context.container.register(DEFAULT_DATASTORE_TOKEN, {
611
137
  useValue: client.datastore(actualName)
612
138
  });
@@ -624,7 +150,6 @@ export {
624
150
  DatastoreClient,
625
151
  DatastoreError,
626
152
  DatastoreLayer,
627
- DynamoDBDatastoreClient,
628
153
  createDatastoreClient,
629
154
  datastoreToken,
630
155
  getDatastore