ag-common 0.0.744 → 0.0.745

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 */
@@ -133,10 +133,10 @@ const queryDynamo = (params) => __awaiter(void 0, void 0, void 0, function* () {
133
133
  items.push(...result.Items);
134
134
  }
135
135
  startKey = result.LastEvaluatedKey;
136
+ // If we have a limit and we've reached it, stop processing
136
137
  if (params.limit && items.length >= params.limit) {
137
138
  return {
138
139
  data: items.slice(0, params.limit),
139
- startKey,
140
140
  };
141
141
  }
142
142
  } while (startKey && Object.keys(startKey).length > 0);
@@ -165,6 +165,138 @@ const scan = (tableName, options) => __awaiter(void 0, void 0, void 0, function*
165
165
  }
166
166
  });
167
167
  exports.scan = scan;
168
+ /**
169
+ * Batch query DynamoDB using PartiQL to query multiple partition key values at once
170
+ *
171
+ * This function uses PartiQL's IN operator to query multiple partition key values
172
+ * in a single request, which is more efficient than making multiple individual queries.
173
+ *
174
+ * Note: AWS has limits on the number of items in WHERE IN queries:
175
+ * - Primary index: 100 items max
176
+ * - Secondary index (GSI/LSI): 50 items max
177
+ * This function automatically chunks requests to stay within these limits.
178
+ *
179
+ * @example
180
+ * // Basic usage with multiple partition keys
181
+ * const result = await batchQueryDynamo<User>({
182
+ * tableName: 'Users',
183
+ * pkName: 'userId',
184
+ * pkValues: ['user1', 'user2', 'user3']
185
+ * });
186
+ *
187
+ * @example
188
+ * // With filter and limit
189
+ * const result = await batchQueryDynamo<Product>({
190
+ * tableName: 'Products',
191
+ * pkName: 'categoryId',
192
+ * pkValues: ['electronics', 'books'],
193
+ * filter: {
194
+ * filterExpression: 'price < :maxPrice',
195
+ * attrNames: {},
196
+ * attrValues: { ':maxPrice': 100 }
197
+ * },
198
+ * limit: 50
199
+ * });
200
+ *
201
+ * @param params - The batch query parameters
202
+ * @returns Promise resolving to query results or error
203
+ */
204
+ const batchQueryDynamo = (params) => __awaiter(void 0, void 0, void 0, function* () {
205
+ try {
206
+ if (params.pkValues.length === 0) {
207
+ return { data: [] };
208
+ }
209
+ // Determine chunk size based on whether we're using a secondary index
210
+ const isSecondaryIndex = !!params.indexName;
211
+ const chunkSize = isSecondaryIndex ? 50 : 100;
212
+ // Chunk the partition key values
213
+ const chunks = [];
214
+ for (let i = 0; i < params.pkValues.length; i += chunkSize) {
215
+ chunks.push(params.pkValues.slice(i, i + chunkSize));
216
+ }
217
+ const allItems = [];
218
+ let totalProcessed = 0;
219
+ // Process each chunk
220
+ for (const chunk of chunks) {
221
+ const result = yield executePartiQLQuery(Object.assign(Object.assign({}, params), { pkValues: chunk }));
222
+ if ('error' in result) {
223
+ return result;
224
+ }
225
+ allItems.push(...result.data);
226
+ totalProcessed += result.data.length;
227
+ // If we have a limit and we've reached it, stop processing
228
+ if (params.limit && totalProcessed >= params.limit) {
229
+ return {
230
+ data: allItems.slice(0, params.limit),
231
+ };
232
+ }
233
+ }
234
+ return {
235
+ data: allItems,
236
+ };
237
+ }
238
+ catch (e) {
239
+ return { error: e.toString() };
240
+ }
241
+ });
242
+ exports.batchQueryDynamo = batchQueryDynamo;
243
+ /**
244
+ * Helper function to execute a single PartiQL query for a chunk of partition keys
245
+ */
246
+ const executePartiQLQuery = (params) => __awaiter(void 0, void 0, void 0, function* () {
247
+ try {
248
+ // Build the PartiQL WHERE clause for multiple partition keys
249
+ const pkPlaceholders = params.pkValues.map(() => '?').join(', ');
250
+ let whereClause = `"${params.pkName}" IN (${pkPlaceholders})`;
251
+ // Build parameters array for PartiQL (positional parameters)
252
+ const parameters = [...params.pkValues];
253
+ // Add filter conditions if provided
254
+ if (params.filter) {
255
+ // For filters, we need to replace the named parameters with positional ones
256
+ let filterExpression = params.filter.filterExpression;
257
+ if (params.filter.attrValues) {
258
+ Object.entries(params.filter.attrValues).forEach(([key, value]) => {
259
+ filterExpression = filterExpression.replace(new RegExp(key.replace(':', '\\:'), 'g'), '?');
260
+ parameters.push(value);
261
+ });
262
+ }
263
+ whereClause += ` AND ${filterExpression}`;
264
+ }
265
+ // Build the PartiQL statement
266
+ const tableName = params.indexName
267
+ ? `"${params.tableName}"."${params.indexName}"`
268
+ : `"${params.tableName}"`;
269
+ const statement = `SELECT * FROM ${tableName} WHERE ${whereClause}`;
270
+ const allItems = [];
271
+ let nextToken;
272
+ // Loop until all results are retrieved
273
+ do {
274
+ const executeParams = {
275
+ Statement: statement,
276
+ Parameters: parameters,
277
+ Limit: params.limit,
278
+ NextToken: nextToken,
279
+ };
280
+ const result = yield (0, withRetry_1.withRetry)(() => _1.dynamoDb.send(new lib_dynamodb_1.ExecuteStatementCommand(executeParams)), 'batchQueryDynamo');
281
+ if (result.Items) {
282
+ allItems.push(...result.Items);
283
+ }
284
+ nextToken = result.NextToken;
285
+ // If we have a limit and we've reached it, stop processing
286
+ if (params.limit && allItems.length >= params.limit) {
287
+ return {
288
+ data: allItems.slice(0, params.limit),
289
+ };
290
+ }
291
+ } while (nextToken);
292
+ return {
293
+ data: allItems,
294
+ };
295
+ }
296
+ catch (e) {
297
+ return { error: e.toString() };
298
+ }
299
+ });
168
300
  function queryWithGenerator(params) {
169
301
  return __asyncGenerator(this, arguments, function* queryWithGenerator_1() {
170
302
  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,15 @@ 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;
42
40
  }
41
+ export interface DynamoBatchQueryParams {
42
+ tableName: string;
43
+ pkName: string;
44
+ pkValues: (string | number)[];
45
+ indexName?: string;
46
+ limit?: number;
47
+ filter?: DynamoFilter;
48
+ }
43
49
  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.745",
3
3
  "name": "ag-common",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",