ag-common 0.0.744 → 0.0.746

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.
@@ -1,6 +1,5 @@
1
- import type { Key } from '../../types';
2
1
  import type { DynamoDBResult } from './types';
3
- import type { DynamoQueryParams, ScanOptions } from './types';
2
+ import type { DynamoBatchQueryParams, DynamoQueryParams, ScanOptions } from './types';
4
3
  export declare const getItemsDynamo: <T>(params: {
5
4
  tableName: string;
6
5
  items: {
@@ -13,13 +12,45 @@ export declare const getItemDynamo: <T>(params: {
13
12
  pkName: string;
14
13
  pkValue: string;
15
14
  }) => Promise<DynamoDBResult<T>>;
16
- export declare const queryDynamo: <T>(params: DynamoQueryParams) => Promise<{
17
- data: T[];
18
- startKey?: Key;
19
- } | {
20
- error: string;
21
- }>;
15
+ export declare const queryDynamo: <T>(params: DynamoQueryParams) => Promise<DynamoDBResult<T[]>>;
22
16
  export declare const scan: <T>(tableName: string, options?: ScanOptions) => Promise<DynamoDBResult<T[]>>;
17
+ /**
18
+ * Batch query DynamoDB using PartiQL to query multiple partition key values at once
19
+ *
20
+ * This function uses PartiQL's IN operator to query multiple partition key values
21
+ * in a single request, which is more efficient than making multiple individual queries.
22
+ *
23
+ * Note: AWS has limits on the number of items in WHERE IN queries:
24
+ * - Primary index: 100 items max
25
+ * - Secondary index (GSI/LSI): 50 items max
26
+ * This function automatically chunks requests to stay within these limits.
27
+ *
28
+ * @example
29
+ * // Basic usage with multiple partition keys
30
+ * const result = await batchQueryDynamo<User>({
31
+ * tableName: 'Users',
32
+ * pkName: 'userId',
33
+ * pkValues: ['user1', 'user2', 'user3']
34
+ * });
35
+ *
36
+ * @example
37
+ * // With filter and limit
38
+ * const result = await batchQueryDynamo<Product>({
39
+ * tableName: 'Products',
40
+ * pkName: 'categoryId',
41
+ * pkValues: ['electronics', 'books'],
42
+ * filter: {
43
+ * filterExpression: 'price < :maxPrice',
44
+ * attrNames: {},
45
+ * attrValues: { ':maxPrice': 100 }
46
+ * },
47
+ * limit: 50
48
+ * });
49
+ *
50
+ * @param params - The batch query parameters
51
+ * @returns Promise resolving to query results or error
52
+ */
53
+ export declare const batchQueryDynamo: <T>(params: DynamoBatchQueryParams) => Promise<DynamoDBResult<T[]>>;
23
54
  export declare function queryWithGenerator<T>(params: DynamoQueryParams & {
24
55
  /** how many to return in query generator. default 100 */
25
56
  BATCH_SIZE?: number;
@@ -22,7 +22,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
22
22
  function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.scan = exports.queryDynamo = exports.getItemDynamo = exports.getItemsDynamo = void 0;
25
+ exports.batchQueryDynamo = exports.scan = exports.queryDynamo = exports.getItemDynamo = exports.getItemsDynamo = void 0;
26
26
  exports.queryWithGenerator = queryWithGenerator;
27
27
  exports.scanWithGenerator = scanWithGenerator;
28
28
  /* eslint-disable no-await-in-loop */
@@ -65,7 +65,9 @@ const executeQuery = (params, startKey) => __awaiter(void 0, void 0, void 0, fun
65
65
  const queryParams = Object.assign({ TableName: params.tableName, KeyConditionExpression: kce, ExpressionAttributeNames: Object.assign(Object.assign({}, ean), (_b = params.filter) === null || _b === void 0 ? void 0 : _b.attrNames), ExpressionAttributeValues: Object.assign(Object.assign({}, eav), (_c = params.filter) === null || _c === void 0 ? void 0 : _c.attrValues), ScanIndexForward: (_d = params.sortAscending) !== null && _d !== void 0 ? _d : true, Limit: (_e = params.BATCH_SIZE) !== null && _e !== void 0 ? _e : params.limit, IndexName: params.indexName, ExclusiveStartKey: startKey }, (params.filter && Object.assign({ FilterExpression: params.filter.filterExpression }, (params.filter.attrValues && {
66
66
  ExpressionAttributeValues: Object.assign(Object.assign({}, eav), params.filter.attrValues),
67
67
  }))));
68
- return (0, withRetry_1.withRetry)(() => _1.dynamoDb.send(new lib_dynamodb_1.QueryCommand(queryParams)), 'queryDynamo');
68
+ return (0, withRetry_1.withRetry)(() => _1.dynamoDb.send(new lib_dynamodb_1.QueryCommand(queryParams)), 'queryDynamo', {
69
+ maxRetries: params.alwaysRetry ? null : undefined,
70
+ });
69
71
  });
70
72
  /**
71
73
  * Helper function that builds the scan parameters and executes the scan
@@ -133,10 +135,10 @@ const queryDynamo = (params) => __awaiter(void 0, void 0, void 0, function* () {
133
135
  items.push(...result.Items);
134
136
  }
135
137
  startKey = result.LastEvaluatedKey;
138
+ // If we have a limit and we've reached it, stop processing
136
139
  if (params.limit && items.length >= params.limit) {
137
140
  return {
138
141
  data: items.slice(0, params.limit),
139
- startKey,
140
142
  };
141
143
  }
142
144
  } while (startKey && Object.keys(startKey).length > 0);
@@ -165,6 +167,138 @@ const scan = (tableName, options) => __awaiter(void 0, void 0, void 0, function*
165
167
  }
166
168
  });
167
169
  exports.scan = scan;
170
+ /**
171
+ * Batch query DynamoDB using PartiQL to query multiple partition key values at once
172
+ *
173
+ * This function uses PartiQL's IN operator to query multiple partition key values
174
+ * in a single request, which is more efficient than making multiple individual queries.
175
+ *
176
+ * Note: AWS has limits on the number of items in WHERE IN queries:
177
+ * - Primary index: 100 items max
178
+ * - Secondary index (GSI/LSI): 50 items max
179
+ * This function automatically chunks requests to stay within these limits.
180
+ *
181
+ * @example
182
+ * // Basic usage with multiple partition keys
183
+ * const result = await batchQueryDynamo<User>({
184
+ * tableName: 'Users',
185
+ * pkName: 'userId',
186
+ * pkValues: ['user1', 'user2', 'user3']
187
+ * });
188
+ *
189
+ * @example
190
+ * // With filter and limit
191
+ * const result = await batchQueryDynamo<Product>({
192
+ * tableName: 'Products',
193
+ * pkName: 'categoryId',
194
+ * pkValues: ['electronics', 'books'],
195
+ * filter: {
196
+ * filterExpression: 'price < :maxPrice',
197
+ * attrNames: {},
198
+ * attrValues: { ':maxPrice': 100 }
199
+ * },
200
+ * limit: 50
201
+ * });
202
+ *
203
+ * @param params - The batch query parameters
204
+ * @returns Promise resolving to query results or error
205
+ */
206
+ const batchQueryDynamo = (params) => __awaiter(void 0, void 0, void 0, function* () {
207
+ try {
208
+ if (params.pkValues.length === 0) {
209
+ return { data: [] };
210
+ }
211
+ // Determine chunk size based on whether we're using a secondary index
212
+ const isSecondaryIndex = !!params.indexName;
213
+ const chunkSize = isSecondaryIndex ? 50 : 100;
214
+ // Chunk the partition key values
215
+ const chunks = [];
216
+ for (let i = 0; i < params.pkValues.length; i += chunkSize) {
217
+ chunks.push(params.pkValues.slice(i, i + chunkSize));
218
+ }
219
+ const allItems = [];
220
+ let totalProcessed = 0;
221
+ // Process each chunk
222
+ for (const chunk of chunks) {
223
+ const result = yield executePartiQLQuery(Object.assign(Object.assign({}, params), { pkValues: chunk }));
224
+ if ('error' in result) {
225
+ return result;
226
+ }
227
+ allItems.push(...result.data);
228
+ totalProcessed += result.data.length;
229
+ // If we have a limit and we've reached it, stop processing
230
+ if (params.limit && totalProcessed >= params.limit) {
231
+ return {
232
+ data: allItems.slice(0, params.limit),
233
+ };
234
+ }
235
+ }
236
+ return {
237
+ data: allItems,
238
+ };
239
+ }
240
+ catch (e) {
241
+ return { error: e.toString() };
242
+ }
243
+ });
244
+ exports.batchQueryDynamo = batchQueryDynamo;
245
+ /**
246
+ * Helper function to execute a single PartiQL query for a chunk of partition keys
247
+ */
248
+ const executePartiQLQuery = (params) => __awaiter(void 0, void 0, void 0, function* () {
249
+ try {
250
+ // Build the PartiQL WHERE clause for multiple partition keys
251
+ const pkPlaceholders = params.pkValues.map(() => '?').join(', ');
252
+ let whereClause = `"${params.pkName}" IN (${pkPlaceholders})`;
253
+ // Build parameters array for PartiQL (positional parameters)
254
+ const parameters = [...params.pkValues];
255
+ // Add filter conditions if provided
256
+ if (params.filter) {
257
+ // For filters, we need to replace the named parameters with positional ones
258
+ let filterExpression = params.filter.filterExpression;
259
+ if (params.filter.attrValues) {
260
+ Object.entries(params.filter.attrValues).forEach(([key, value]) => {
261
+ filterExpression = filterExpression.replace(new RegExp(key.replace(':', '\\:'), 'g'), '?');
262
+ parameters.push(value);
263
+ });
264
+ }
265
+ whereClause += ` AND ${filterExpression}`;
266
+ }
267
+ // Build the PartiQL statement
268
+ const tableName = params.indexName
269
+ ? `"${params.tableName}"."${params.indexName}"`
270
+ : `"${params.tableName}"`;
271
+ const statement = `SELECT * FROM ${tableName} WHERE ${whereClause}`;
272
+ const allItems = [];
273
+ let nextToken;
274
+ // Loop until all results are retrieved
275
+ do {
276
+ const executeParams = {
277
+ Statement: statement,
278
+ Parameters: parameters,
279
+ Limit: params.limit,
280
+ NextToken: nextToken,
281
+ };
282
+ const result = yield (0, withRetry_1.withRetry)(() => _1.dynamoDb.send(new lib_dynamodb_1.ExecuteStatementCommand(executeParams)), 'batchQueryDynamo');
283
+ if (result.Items) {
284
+ allItems.push(...result.Items);
285
+ }
286
+ nextToken = result.NextToken;
287
+ // If we have a limit and we've reached it, stop processing
288
+ if (params.limit && allItems.length >= params.limit) {
289
+ return {
290
+ data: allItems.slice(0, params.limit),
291
+ };
292
+ }
293
+ } while (nextToken);
294
+ return {
295
+ data: allItems,
296
+ };
297
+ }
298
+ catch (e) {
299
+ return { error: e.toString() };
300
+ }
301
+ });
168
302
  function queryWithGenerator(params) {
169
303
  return __asyncGenerator(this, arguments, function* queryWithGenerator_1() {
170
304
  var _a;
@@ -19,12 +19,7 @@ export declare const getItemDynamo: <T>(params: {
19
19
  pkName: string;
20
20
  pkValue: string;
21
21
  }[];
22
- }) => Promise<import("./types").DynamoDBResult<T[]>>, queryDynamo: <T>(params: import("./types").DynamoQueryParams) => Promise<{
23
- data: T[];
24
- startKey?: import("../..").Key;
25
- } | {
26
- error: string;
27
- }>, queryWithGenerator: typeof getOps.queryWithGenerator, scan: <T>(tableName: string, options?: import("./types").ScanOptions) => Promise<import("./types").DynamoDBResult<T[]>>, scanWithGenerator: typeof getOps.scanWithGenerator;
22
+ }) => Promise<import("./types").DynamoDBResult<T[]>>, queryDynamo: <T>(params: import("./types").DynamoQueryParams) => Promise<import("./types").DynamoDBResult<T[]>>, batchQueryDynamo: <T>(params: import("./types").DynamoBatchQueryParams) => Promise<import("./types").DynamoDBResult<T[]>>, queryWithGenerator: typeof getOps.queryWithGenerator, scan: <T>(tableName: string, options?: import("./types").ScanOptions) => Promise<import("./types").DynamoDBResult<T[]>>, scanWithGenerator: typeof getOps.scanWithGenerator;
28
23
  export declare const putDynamo: <T extends Record<string, unknown>>(item: T, tableName: string, opt?: {
29
24
  pkName?: string;
30
25
  }) => Promise<import("./types").DynamoDBResult<void>>, batchWrite: <T extends Record<string, unknown>>(tableName: string, items: T[], opt?: {
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.wipeTable = exports.batchDelete = exports.getDynamoUpdates = exports.batchWrite = exports.putDynamo = exports.scanWithGenerator = exports.scan = exports.queryWithGenerator = exports.queryDynamo = exports.getItemsDynamo = exports.getItemDynamo = exports.setDynamo = exports.dynamoDb = void 0;
39
+ exports.wipeTable = exports.batchDelete = exports.getDynamoUpdates = exports.batchWrite = exports.putDynamo = exports.scanWithGenerator = exports.scan = exports.queryWithGenerator = exports.batchQueryDynamo = exports.queryDynamo = exports.getItemsDynamo = exports.getItemDynamo = exports.setDynamo = exports.dynamoDb = void 0;
40
40
  const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
41
41
  const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
42
42
  const deleteOps = __importStar(require("./delete"));
@@ -59,7 +59,7 @@ exports.setDynamo = setDynamo;
59
59
  // Initialize with default region
60
60
  exports.dynamoDb = (0, exports.setDynamo)('ap-southeast-2');
61
61
  // Export all operations
62
- exports.getItemDynamo = getOps.getItemDynamo, exports.getItemsDynamo = getOps.getItemsDynamo, exports.queryDynamo = getOps.queryDynamo, exports.queryWithGenerator = getOps.queryWithGenerator, exports.scan = getOps.scan, exports.scanWithGenerator = getOps.scanWithGenerator;
62
+ exports.getItemDynamo = getOps.getItemDynamo, exports.getItemsDynamo = getOps.getItemsDynamo, exports.queryDynamo = getOps.queryDynamo, exports.batchQueryDynamo = getOps.batchQueryDynamo, exports.queryWithGenerator = getOps.queryWithGenerator, exports.scan = getOps.scan, exports.scanWithGenerator = getOps.scanWithGenerator;
63
63
  exports.putDynamo = setOps.putDynamo, exports.batchWrite = setOps.batchWrite, exports.getDynamoUpdates = setOps.getDynamoUpdates;
64
64
  exports.batchDelete = deleteOps.batchDelete, exports.wipeTable = deleteOps.wipeTable;
65
65
  // Export types
@@ -1,4 +1,3 @@
1
- import type { Key } from '../../types';
2
1
  export type DynamoDBError = {
3
2
  error: string;
4
3
  };
@@ -36,8 +35,16 @@ export interface DynamoQueryParams {
36
35
  skOperator?: '=' | '<' | '>' | '<=' | '>=' | 'BETWEEN' | 'BEGINS_WITH';
37
36
  indexName?: string;
38
37
  limit?: number;
39
- startKey?: Key;
40
38
  filter?: DynamoFilter;
41
39
  sortAscending?: boolean;
40
+ alwaysRetry?: boolean;
41
+ }
42
+ export interface DynamoBatchQueryParams {
43
+ tableName: string;
44
+ pkName: string;
45
+ pkValues: (string | number)[];
46
+ indexName?: string;
47
+ limit?: number;
48
+ filter?: DynamoFilter;
42
49
  }
43
50
  export declare const isError: <T>(result: DynamoDBResult<T>) => result is DynamoDBError;
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.withRetry = void 0;
13
+ /* eslint-disable no-await-in-loop */
13
14
  const log_1 = require("../../common/helpers/log");
14
15
  const sleep_1 = require("../../common/helpers/sleep");
15
16
  const withRetry = (operation, operationName, opt) => __awaiter(void 0, void 0, void 0, function* () {
@@ -23,10 +24,11 @@ const withRetry = (operation, operationName, opt) => __awaiter(void 0, void 0, v
23
24
  }
24
25
  catch (e) {
25
26
  const error = e;
26
- const errorString = error.toString().toLowerCase();
27
+ const errorString = error.toString().toLowerCase().replace(/\s+/gim, '');
27
28
  if (errorString.includes('429') ||
28
29
  errorString.includes('provisionedthroughputexceeded') ||
29
- errorString.includes('too large')) {
30
+ errorString.includes('toolarge') ||
31
+ errorString.includes('ratelimited')) {
30
32
  retryCount++;
31
33
  if (maxRetries !== null && retryCount >= maxRetries) {
32
34
  (0, log_1.warn)(`${operationName}: Max retries exceeded`);
@@ -22,6 +22,7 @@ const BarChartBase = styled_1.default.div `
22
22
  const ItemStyled = (0, styled_1.default)(Item_1.Item) `
23
23
  margin-bottom: 0.75rem;
24
24
  height: auto;
25
+ overflow: hidden;
25
26
 
26
27
  &:last-of-type {
27
28
  margin-bottom: 0;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.744",
2
+ "version": "0.0.746",
3
3
  "name": "ag-common",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",