quidproquo-actionprocessor-awslambda 0.1.14 → 0.1.16

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.
Files changed (39) hide show
  1. package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.d.ts +2 -0
  2. package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js +64 -0
  3. package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js.map +1 -0
  4. package/lib/commonjs/getActionProcessor/core/keyValueStore/index.js +2 -1
  5. package/lib/commonjs/getActionProcessor/core/keyValueStore/index.js.map +1 -1
  6. package/lib/commonjs/logic/dynamo/batchPutItems.d.ts +5 -0
  7. package/lib/commonjs/logic/dynamo/batchPutItems.js +62 -0
  8. package/lib/commonjs/logic/dynamo/batchPutItems.js.map +1 -0
  9. package/lib/commonjs/logic/dynamo/index.d.ts +1 -0
  10. package/lib/commonjs/logic/dynamo/index.js +1 -0
  11. package/lib/commonjs/logic/dynamo/index.js.map +1 -1
  12. package/lib/commonjs/logic/dynamo/putItem.js +1 -7
  13. package/lib/commonjs/logic/dynamo/putItem.js.map +1 -1
  14. package/lib/commonjs/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.d.ts +3 -0
  15. package/lib/commonjs/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.js +12 -0
  16. package/lib/commonjs/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.js.map +1 -0
  17. package/lib/commonjs/logic/dynamo/qpqDynamoOrm/index.d.ts +1 -0
  18. package/lib/commonjs/logic/dynamo/qpqDynamoOrm/index.js +1 -0
  19. package/lib/commonjs/logic/dynamo/qpqDynamoOrm/index.js.map +1 -1
  20. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.d.ts +2 -0
  21. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js +49 -0
  22. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js.map +1 -0
  23. package/lib/esm/getActionProcessor/core/keyValueStore/index.js +2 -0
  24. package/lib/esm/getActionProcessor/core/keyValueStore/index.js.map +1 -1
  25. package/lib/esm/logic/dynamo/batchPutItems.d.ts +5 -0
  26. package/lib/esm/logic/dynamo/batchPutItems.js +45 -0
  27. package/lib/esm/logic/dynamo/batchPutItems.js.map +1 -0
  28. package/lib/esm/logic/dynamo/index.d.ts +1 -0
  29. package/lib/esm/logic/dynamo/index.js +1 -0
  30. package/lib/esm/logic/dynamo/index.js.map +1 -1
  31. package/lib/esm/logic/dynamo/putItem.js +1 -7
  32. package/lib/esm/logic/dynamo/putItem.js.map +1 -1
  33. package/lib/esm/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.d.ts +3 -0
  34. package/lib/esm/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.js +8 -0
  35. package/lib/esm/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.js.map +1 -0
  36. package/lib/esm/logic/dynamo/qpqDynamoOrm/index.d.ts +1 -0
  37. package/lib/esm/logic/dynamo/qpqDynamoOrm/index.js +1 -0
  38. package/lib/esm/logic/dynamo/qpqDynamoOrm/index.js.map +1 -1
  39. package/package.json +7 -7
@@ -0,0 +1,2 @@
1
+ import { ActionProcessorListResolver } from 'quidproquo-core';
2
+ export declare const getKeyValueStoreUpsertManyActionProcessor: ActionProcessorListResolver;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.getKeyValueStoreUpsertManyActionProcessor = void 0;
13
+ const quidproquo_config_aws_1 = require("quidproquo-config-aws");
14
+ const quidproquo_core_1 = require("quidproquo-core");
15
+ const awsNamingUtils_1 = require("../../../awsNamingUtils");
16
+ const dynamo_1 = require("../../../logic/dynamo");
17
+ // The batch sibling of Upsert: BatchWriteItem in chunks of 25, partial-acceptance
18
+ // retry inside batchPutItems. Streams emit one record per item no matter how it
19
+ // was written, so downstream projectors see the same records as N single upserts.
20
+ // Unconditional (no ifNotExists — BatchWriteItem carries no conditions).
21
+ const getProcessKeyValueStoreUpsertMany = (qpqConfig) => {
22
+ return (_a) => __awaiter(void 0, [_a], void 0, function* ({ keyValueStoreName, items, options }) {
23
+ const dynamoTableName = (0, awsNamingUtils_1.getKvsDynamoTableNameFromConfig)(keyValueStoreName, qpqConfig, 'kvs');
24
+ const region = quidproquo_config_aws_1.qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
25
+ try {
26
+ const storeConfig = (0, quidproquo_core_1.resolveKvsStoreConfigOrThrow)(qpqConfig, keyValueStoreName);
27
+ // Same-key duplicates are rejected UP FRONT, before anything is written.
28
+ // Dynamo itself only rejects them when both land in the same 25-item
29
+ // chunk — across chunks the later write silently wins — so left to the
30
+ // service the outcome would depend on item position.
31
+ const seenKeys = new Set();
32
+ for (const item of items) {
33
+ const itemKey = [storeConfig.partitionKey, ...storeConfig.sortKeys].map((key) => String(item[key.key])).join('#');
34
+ if (seenKeys.has(itemKey)) {
35
+ return (0, quidproquo_core_1.actionResultError)(quidproquo_core_1.KeyValueStoreUpsertManyErrorTypeEnum.DuplicateKey, `Duplicate key [${itemKey}] in batch upsert to [${keyValueStoreName}]`);
36
+ }
37
+ seenKeys.add(itemKey);
38
+ }
39
+ // The scope lives inside the stored partition key value, so each item is
40
+ // persisted with a composed pk; reads strip it back off.
41
+ const scoped = (0, quidproquo_core_1.getScopedKvsTranslatorOrThrow)(qpqConfig, keyValueStoreName, options === null || options === void 0 ? void 0 : options.scope);
42
+ yield (0, dynamo_1.batchPutItems)(dynamoTableName, items.map((item) => scoped.item(item)), region);
43
+ return (0, quidproquo_core_1.actionResult)(void 0);
44
+ }
45
+ catch (error) {
46
+ return (0, quidproquo_core_1.actionResultErrorFromCaughtError)(error, {
47
+ InternalServerError: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_1.KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS Service Unavailable'),
48
+ ResourceNotFoundException: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_1.KeyValueStoreUpsertManyErrorTypeEnum.ResourceNotFound, 'KVS Resource Not Found'),
49
+ // Retries exhausted on UnprocessedItems — sustained throttle, a transient
50
+ // fault: callers must see ServiceUnavailable, not a permanent failure.
51
+ BatchWriteUnprocessedItemsError: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_1.KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS batch write throttled'),
52
+ InvalidScopeError: (error) => (0, quidproquo_core_1.actionResultError)(quidproquo_core_1.KeyValueStoreUpsertManyErrorTypeEnum.InvalidScope, error.message),
53
+ KvsStoreNotFoundError: (error) => (0, quidproquo_core_1.actionResultError)(quidproquo_core_1.KeyValueStoreUpsertManyErrorTypeEnum.StoreNotFound, error.message),
54
+ });
55
+ }
56
+ });
57
+ };
58
+ const getKeyValueStoreUpsertManyActionProcessor = (qpqConfig) => __awaiter(void 0, void 0, void 0, function* () {
59
+ return ({
60
+ [quidproquo_core_1.KeyValueStoreActionType.UpsertMany]: getProcessKeyValueStoreUpsertMany(qpqConfig),
61
+ });
62
+ });
63
+ exports.getKeyValueStoreUpsertManyActionProcessor = getKeyValueStoreUpsertManyActionProcessor;
64
+ //# sourceMappingURL=getKeyValueStoreUpsertManyActionProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getKeyValueStoreUpsertManyActionProcessor.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,iEAA0D;AAC1D,qDAYyB;AAEzB,4DAA0E;AAC1E,kDAAsD;AAEtD,kFAAkF;AAClF,gFAAgF;AAChF,kFAAkF;AAClF,yEAAyE;AACzE,MAAM,iCAAiC,GAAG,CAAC,SAAoB,EAA+C,EAAE;IAC9G,OAAO,KAA8C,EAAE,4CAAzC,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE;QACjD,MAAM,eAAe,GAAG,IAAA,gDAA+B,EAAC,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC7F,MAAM,MAAM,GAAG,yCAAiB,CAAC,gCAAgC,CAAC,SAAS,CAAC,CAAC;QAE7E,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,IAAA,8CAA4B,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;YAE/E,yEAAyE;YACzE,qEAAqE;YACrE,uEAAuE;YACvE,qDAAqD;YACrD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;YACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,CAAC,WAAW,CAAC,YAAY,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAClH,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC1B,OAAO,IAAA,mCAAiB,EACtB,sDAAoC,CAAC,YAAY,EACjD,kBAAkB,OAAO,yBAAyB,iBAAiB,GAAG,CACvE,CAAC;gBACJ,CAAC;gBACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;YAED,yEAAyE;YACzE,yDAAyD;YACzD,MAAM,MAAM,GAAG,IAAA,+CAA6B,EAAC,SAAS,EAAE,iBAAiB,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,CAAC,CAAC;YAE3F,MAAM,IAAA,sBAAa,EACjB,eAAe,EACf,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EACtC,MAAM,CACP,CAAC;YAEF,OAAO,IAAA,8BAAY,EAAC,KAAK,CAAC,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,OAAO,IAAA,kDAAgC,EAAC,KAAK,EAAE;gBAC7C,mBAAmB,EAAE,GAAG,EAAE,CAAC,IAAA,mCAAiB,EAAC,sDAAoC,CAAC,kBAAkB,EAAE,yBAAyB,CAAC;gBAChI,yBAAyB,EAAE,GAAG,EAAE,CAAC,IAAA,mCAAiB,EAAC,sDAAoC,CAAC,gBAAgB,EAAE,wBAAwB,CAAC;gBACnI,0EAA0E;gBAC1E,uEAAuE;gBACvE,+BAA+B,EAAE,GAAG,EAAE,CACpC,IAAA,mCAAiB,EAAC,sDAAoC,CAAC,kBAAkB,EAAE,2BAA2B,CAAC;gBACzG,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,mCAAiB,EAAC,sDAAoC,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC;gBACjH,qBAAqB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,mCAAiB,EAAC,sDAAoC,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC;aACvH,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAA,CAAC;AACJ,CAAC,CAAC;AAEK,MAAM,yCAAyC,GAAgC,CAAO,SAAoB,EAAgC,EAAE;IAAC,OAAA,CAAC;QACnJ,CAAC,yCAAuB,CAAC,UAAU,CAAC,EAAE,iCAAiC,CAAC,SAAS,CAAC;KACnF,CAAC,CAAA;EAAA,CAAC;AAFU,QAAA,yCAAyC,6CAEnD","sourcesContent":["import { qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport {\n ActionProcessorList,\n ActionProcessorListResolver,\n actionResult,\n actionResultError,\n actionResultErrorFromCaughtError,\n getScopedKvsTranslatorOrThrow,\n KeyValueStoreActionType,\n KeyValueStoreUpsertManyActionProcessor,\n KeyValueStoreUpsertManyErrorTypeEnum,\n QPQConfig,\n resolveKvsStoreConfigOrThrow,\n} from 'quidproquo-core';\n\nimport { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';\nimport { batchPutItems } from '../../../logic/dynamo';\n\n// The batch sibling of Upsert: BatchWriteItem in chunks of 25, partial-acceptance\n// retry inside batchPutItems. Streams emit one record per item no matter how it\n// was written, so downstream projectors see the same records as N single upserts.\n// Unconditional (no ifNotExists — BatchWriteItem carries no conditions).\nconst getProcessKeyValueStoreUpsertMany = (qpqConfig: QPQConfig): KeyValueStoreUpsertManyActionProcessor<any> => {\n return async ({ keyValueStoreName, items, options }) => {\n const dynamoTableName = getKvsDynamoTableNameFromConfig(keyValueStoreName, qpqConfig, 'kvs');\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n\n try {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n\n // Same-key duplicates are rejected UP FRONT, before anything is written.\n // Dynamo itself only rejects them when both land in the same 25-item\n // chunk — across chunks the later write silently wins — so left to the\n // service the outcome would depend on item position.\n const seenKeys = new Set<string>();\n for (const item of items) {\n const itemKey = [storeConfig.partitionKey, ...storeConfig.sortKeys].map((key) => String(item[key.key])).join('#');\n if (seenKeys.has(itemKey)) {\n return actionResultError(\n KeyValueStoreUpsertManyErrorTypeEnum.DuplicateKey,\n `Duplicate key [${itemKey}] in batch upsert to [${keyValueStoreName}]`,\n );\n }\n seenKeys.add(itemKey);\n }\n\n // The scope lives inside the stored partition key value, so each item is\n // persisted with a composed pk; reads strip it back off.\n const scoped = getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, options?.scope);\n\n await batchPutItems(\n dynamoTableName,\n items.map((item) => scoped.item(item)),\n region,\n );\n\n return actionResult(void 0);\n } catch (error: unknown) {\n return actionResultErrorFromCaughtError(error, {\n InternalServerError: () => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS Service Unavailable'),\n ResourceNotFoundException: () => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ResourceNotFound, 'KVS Resource Not Found'),\n // Retries exhausted on UnprocessedItems — sustained throttle, a transient\n // fault: callers must see ServiceUnavailable, not a permanent failure.\n BatchWriteUnprocessedItemsError: () =>\n actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS batch write throttled'),\n InvalidScopeError: (error) => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.InvalidScope, error.message),\n KvsStoreNotFoundError: (error) => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.StoreNotFound, error.message),\n });\n }\n };\n};\n\nexport const getKeyValueStoreUpsertManyActionProcessor: ActionProcessorListResolver = async (qpqConfig: QPQConfig): Promise<ActionProcessorList> => ({\n [KeyValueStoreActionType.UpsertMany]: getProcessKeyValueStoreUpsertMany(qpqConfig),\n});\n"]}
@@ -18,8 +18,9 @@ const getKeyValueStoreScanActionProcessor_1 = require("./getKeyValueStoreScanAct
18
18
  const getKeyValueStoreScanAllScopesActionProcessor_1 = require("./getKeyValueStoreScanAllScopesActionProcessor");
19
19
  const getKeyValueStoreUpdateActionProcessor_1 = require("./getKeyValueStoreUpdateActionProcessor");
20
20
  const getKeyValueStoreUpsertActionProcessor_1 = require("./getKeyValueStoreUpsertActionProcessor");
21
+ const getKeyValueStoreUpsertManyActionProcessor_1 = require("./getKeyValueStoreUpsertManyActionProcessor");
21
22
  const getKeyValueStoreActionProcessor = (qpqConfig, dynamicModuleLoader) => __awaiter(void 0, void 0, void 0, function* () {
22
- return (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (yield (0, getKeyValueStoreDeleteActionProcessor_1.getKeyValueStoreDeleteActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreGetActionProcessor_1.getKeyValueStoreGetActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreGetAllActionProcessor_1.getKeyValueStoreGetAllActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreUpsertActionProcessor_1.getKeyValueStoreUpsertActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreUpdateActionProcessor_1.getKeyValueStoreUpdateActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreQueryActionProcessor_1.getKeyValueStoreQueryActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreScanActionProcessor_1.getKeyValueStoreScanActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreScanAllScopesActionProcessor_1.getKeyValueStoreScanAllScopesActionProcessor)(qpqConfig, dynamicModuleLoader))));
23
+ return (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (yield (0, getKeyValueStoreDeleteActionProcessor_1.getKeyValueStoreDeleteActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreGetActionProcessor_1.getKeyValueStoreGetActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreGetAllActionProcessor_1.getKeyValueStoreGetAllActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreUpsertActionProcessor_1.getKeyValueStoreUpsertActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreUpsertManyActionProcessor_1.getKeyValueStoreUpsertManyActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreUpdateActionProcessor_1.getKeyValueStoreUpdateActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreQueryActionProcessor_1.getKeyValueStoreQueryActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreScanActionProcessor_1.getKeyValueStoreScanActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, getKeyValueStoreScanAllScopesActionProcessor_1.getKeyValueStoreScanAllScopesActionProcessor)(qpqConfig, dynamicModuleLoader))));
23
24
  });
24
25
  exports.getKeyValueStoreActionProcessor = getKeyValueStoreActionProcessor;
25
26
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,mGAAgG;AAChG,6FAA0F;AAC1F,mGAAgG;AAChG,iGAA8F;AAC9F,+FAA4F;AAC5F,iHAA8G;AAC9G,mGAAgG;AAChG,mGAAgG;AAEzF,MAAM,+BAA+B,GAAgC,CAC1E,SAAoB,EACpB,mBAAwC,EACV,EAAE;IAAC,OAAA,qHAC9B,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,uEAAkC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC1E,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,2EAAoC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC5E,CAAC,MAAM,IAAA,yEAAmC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC3E,CAAC,MAAM,IAAA,2FAA4C,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,EACvF,CAAA;EAAA,CAAC;AAZU,QAAA,+BAA+B,mCAYzC","sourcesContent":["import { ActionProcessorList, ActionProcessorListResolver, DynamicModuleLoader, QPQConfig } from 'quidproquo-core';\n\nimport { getKeyValueStoreDeleteActionProcessor } from './getKeyValueStoreDeleteActionProcessor';\nimport { getKeyValueStoreGetActionProcessor } from './getKeyValueStoreGetActionProcessor';\nimport { getKeyValueStoreGetAllActionProcessor } from './getKeyValueStoreGetAllActionProcessor';\nimport { getKeyValueStoreQueryActionProcessor } from './getKeyValueStoreQueryActionProcessor';\nimport { getKeyValueStoreScanActionProcessor } from './getKeyValueStoreScanActionProcessor';\nimport { getKeyValueStoreScanAllScopesActionProcessor } from './getKeyValueStoreScanAllScopesActionProcessor';\nimport { getKeyValueStoreUpdateActionProcessor } from './getKeyValueStoreUpdateActionProcessor';\nimport { getKeyValueStoreUpsertActionProcessor } from './getKeyValueStoreUpsertActionProcessor';\n\nexport const getKeyValueStoreActionProcessor: ActionProcessorListResolver = async (\n qpqConfig: QPQConfig,\n dynamicModuleLoader: DynamicModuleLoader,\n): Promise<ActionProcessorList> => ({\n ...(await getKeyValueStoreDeleteActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetAllActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpsertActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpdateActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreQueryActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanAllScopesActionProcessor(qpqConfig, dynamicModuleLoader)),\n});\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,mGAAgG;AAChG,6FAA0F;AAC1F,mGAAgG;AAChG,iGAA8F;AAC9F,+FAA4F;AAC5F,iHAA8G;AAC9G,mGAAgG;AAChG,mGAAgG;AAChG,2GAAwG;AAEjG,MAAM,+BAA+B,GAAgC,CAC1E,SAAoB,EACpB,mBAAwC,EACV,EAAE;IAAC,OAAA,mIAC9B,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,uEAAkC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC1E,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,qFAAyC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GACjF,CAAC,MAAM,IAAA,6EAAqC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC7E,CAAC,MAAM,IAAA,2EAAoC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC5E,CAAC,MAAM,IAAA,yEAAmC,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,GAC3E,CAAC,MAAM,IAAA,2FAA4C,EAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,EACvF,CAAA;EAAA,CAAC;AAbU,QAAA,+BAA+B,mCAazC","sourcesContent":["import { ActionProcessorList, ActionProcessorListResolver, DynamicModuleLoader, QPQConfig } from 'quidproquo-core';\n\nimport { getKeyValueStoreDeleteActionProcessor } from './getKeyValueStoreDeleteActionProcessor';\nimport { getKeyValueStoreGetActionProcessor } from './getKeyValueStoreGetActionProcessor';\nimport { getKeyValueStoreGetAllActionProcessor } from './getKeyValueStoreGetAllActionProcessor';\nimport { getKeyValueStoreQueryActionProcessor } from './getKeyValueStoreQueryActionProcessor';\nimport { getKeyValueStoreScanActionProcessor } from './getKeyValueStoreScanActionProcessor';\nimport { getKeyValueStoreScanAllScopesActionProcessor } from './getKeyValueStoreScanAllScopesActionProcessor';\nimport { getKeyValueStoreUpdateActionProcessor } from './getKeyValueStoreUpdateActionProcessor';\nimport { getKeyValueStoreUpsertActionProcessor } from './getKeyValueStoreUpsertActionProcessor';\nimport { getKeyValueStoreUpsertManyActionProcessor } from './getKeyValueStoreUpsertManyActionProcessor';\n\nexport const getKeyValueStoreActionProcessor: ActionProcessorListResolver = async (\n qpqConfig: QPQConfig,\n dynamicModuleLoader: DynamicModuleLoader,\n): Promise<ActionProcessorList> => ({\n ...(await getKeyValueStoreDeleteActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetAllActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpsertActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpsertManyActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpdateActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreQueryActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanAllScopesActionProcessor(qpqConfig, dynamicModuleLoader)),\n});\n"]}
@@ -0,0 +1,5 @@
1
+ import { KvsObjectDataType } from 'quidproquo-core';
2
+ export declare class BatchWriteUnprocessedItemsError extends Error {
3
+ constructor(tableName: string, unprocessedCount: number, attempts: number);
4
+ }
5
+ export declare function batchPutItems(tableName: string, items: KvsObjectDataType[], region: string): Promise<void>;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.BatchWriteUnprocessedItemsError = void 0;
13
+ exports.batchPutItems = batchPutItems;
14
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
15
+ const createAwsClient_1 = require("../createAwsClient");
16
+ const qpqDynamoOrm_1 = require("./qpqDynamoOrm");
17
+ // BatchWriteItem accepts at most 25 writes per call.
18
+ const BATCH_WRITE_MAX_ITEMS = 25;
19
+ // UnprocessedItems is DynamoDB's throttle back-pressure, not an error: the
20
+ // service accepted part of the batch and hands the rest back for resubmission.
21
+ // Retry with backoff; only a batch that STAYS unprocessed is a real failure.
22
+ const MAX_UNPROCESSED_RETRIES = 8;
23
+ const RETRY_BASE_DELAY_MS = 50;
24
+ // Errors are matched by NAME in the processors' error tables, so the name is
25
+ // the API. Exhausted retries mean sustained throttle — a transient fault the
26
+ // processor must classify as ServiceUnavailable, never a generic (permanent)
27
+ // failure.
28
+ class BatchWriteUnprocessedItemsError extends Error {
29
+ constructor(tableName, unprocessedCount, attempts) {
30
+ super(`BatchWriteItem to [${tableName}] still has ${unprocessedCount} unprocessed items after ${attempts} attempts`);
31
+ this.name = 'BatchWriteUnprocessedItemsError';
32
+ }
33
+ }
34
+ exports.BatchWriteUnprocessedItemsError = BatchWriteUnprocessedItemsError;
35
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
36
+ // Unconditional batched puts. No ConditionExpression exists on BatchWriteItem,
37
+ // so unlike putItem there is no ifNotExists — callers must only batch items
38
+ // whose keys are unique by construction (see askKeyValueStoreUpsertMany).
39
+ function batchPutItems(tableName, items, region) {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ var _a, _b;
42
+ const dynamoDBClient = (0, createAwsClient_1.createAwsClient)(client_dynamodb_1.DynamoDBClient, { region });
43
+ for (let offset = 0; offset < items.length; offset += BATCH_WRITE_MAX_ITEMS) {
44
+ let writeRequests = items
45
+ .slice(offset, offset + BATCH_WRITE_MAX_ITEMS)
46
+ .map((item) => ({ PutRequest: { Item: (0, qpqDynamoOrm_1.convertObjectToDynamoItem)(item) } }));
47
+ for (let attempt = 0; writeRequests.length > 0; attempt += 1) {
48
+ if (attempt > MAX_UNPROCESSED_RETRIES) {
49
+ throw new BatchWriteUnprocessedItemsError(tableName, writeRequests.length, attempt);
50
+ }
51
+ if (attempt > 0) {
52
+ yield delay(RETRY_BASE_DELAY_MS * Math.pow(2, (attempt - 1)));
53
+ }
54
+ const response = yield dynamoDBClient.send(new client_dynamodb_1.BatchWriteItemCommand({
55
+ RequestItems: { [tableName]: writeRequests },
56
+ }));
57
+ writeRequests = (_b = (_a = response.UnprocessedItems) === null || _a === void 0 ? void 0 : _a[tableName]) !== null && _b !== void 0 ? _b : [];
58
+ }
59
+ }
60
+ });
61
+ }
62
+ //# sourceMappingURL=batchPutItems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batchPutItems.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/batchPutItems.ts"],"names":[],"mappings":";;;;;;;;;;;;AAgCA,sCA0BC;AAxDD,8DAA+F;AAE/F,wDAAqD;AACrD,iDAA2D;AAE3D,qDAAqD;AACrD,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,2EAA2E;AAC3E,+EAA+E;AAC/E,6EAA6E;AAC7E,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAClC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,WAAW;AACX,MAAa,+BAAgC,SAAQ,KAAK;IACxD,YAAY,SAAiB,EAAE,gBAAwB,EAAE,QAAgB;QACvE,KAAK,CAAC,sBAAsB,SAAS,eAAe,gBAAgB,4BAA4B,QAAQ,WAAW,CAAC,CAAC;QACrH,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;IAChD,CAAC;CACF;AALD,0EAKC;AAED,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEhF,+EAA+E;AAC/E,4EAA4E;AAC5E,0EAA0E;AAC1E,SAAsB,aAAa,CAAC,SAAiB,EAAE,KAA0B,EAAE,MAAc;;;QAC/F,MAAM,cAAc,GAAG,IAAA,iCAAe,EAAC,gCAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAEnE,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,qBAAqB,EAAE,CAAC;YAC5E,IAAI,aAAa,GAAmB,KAAK;iBACtC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAAC;iBAC7C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,IAAA,wCAAyB,EAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAE9E,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;gBAC7D,IAAI,OAAO,GAAG,uBAAuB,EAAE,CAAC;oBACtC,MAAM,IAAI,+BAA+B,CAAC,SAAS,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACtF,CAAC;gBAED,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,MAAM,KAAK,CAAC,mBAAmB,GAAG,SAAA,CAAC,EAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA,CAAC,CAAC;gBACxD,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,CACxC,IAAI,uCAAqB,CAAC;oBACxB,YAAY,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE;iBAC7C,CAAC,CACH,CAAC;gBAEF,aAAa,GAAG,MAAA,MAAA,QAAQ,CAAC,gBAAgB,0CAAG,SAAS,CAAC,mCAAI,EAAE,CAAC;YAC/D,CAAC;QACH,CAAC;IACH,CAAC;CAAA","sourcesContent":["import { KvsObjectDataType } from 'quidproquo-core';\n\nimport { BatchWriteItemCommand, DynamoDBClient, WriteRequest } from '@aws-sdk/client-dynamodb';\n\nimport { createAwsClient } from '../createAwsClient';\nimport { convertObjectToDynamoItem } from './qpqDynamoOrm';\n\n// BatchWriteItem accepts at most 25 writes per call.\nconst BATCH_WRITE_MAX_ITEMS = 25;\n\n// UnprocessedItems is DynamoDB's throttle back-pressure, not an error: the\n// service accepted part of the batch and hands the rest back for resubmission.\n// Retry with backoff; only a batch that STAYS unprocessed is a real failure.\nconst MAX_UNPROCESSED_RETRIES = 8;\nconst RETRY_BASE_DELAY_MS = 50;\n\n// Errors are matched by NAME in the processors' error tables, so the name is\n// the API. Exhausted retries mean sustained throttle — a transient fault the\n// processor must classify as ServiceUnavailable, never a generic (permanent)\n// failure.\nexport class BatchWriteUnprocessedItemsError extends Error {\n constructor(tableName: string, unprocessedCount: number, attempts: number) {\n super(`BatchWriteItem to [${tableName}] still has ${unprocessedCount} unprocessed items after ${attempts} attempts`);\n this.name = 'BatchWriteUnprocessedItemsError';\n }\n}\n\nconst delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n// Unconditional batched puts. No ConditionExpression exists on BatchWriteItem,\n// so unlike putItem there is no ifNotExists — callers must only batch items\n// whose keys are unique by construction (see askKeyValueStoreUpsertMany).\nexport async function batchPutItems(tableName: string, items: KvsObjectDataType[], region: string): Promise<void> {\n const dynamoDBClient = createAwsClient(DynamoDBClient, { region });\n\n for (let offset = 0; offset < items.length; offset += BATCH_WRITE_MAX_ITEMS) {\n let writeRequests: WriteRequest[] = items\n .slice(offset, offset + BATCH_WRITE_MAX_ITEMS)\n .map((item) => ({ PutRequest: { Item: convertObjectToDynamoItem(item) } }));\n\n for (let attempt = 0; writeRequests.length > 0; attempt += 1) {\n if (attempt > MAX_UNPROCESSED_RETRIES) {\n throw new BatchWriteUnprocessedItemsError(tableName, writeRequests.length, attempt);\n }\n\n if (attempt > 0) {\n await delay(RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));\n }\n\n const response = await dynamoDBClient.send(\n new BatchWriteItemCommand({\n RequestItems: { [tableName]: writeRequests },\n }),\n );\n\n writeRequests = response.UnprocessedItems?.[tableName] ?? [];\n }\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export * from './batchPutItems';
1
2
  export * from './convertDynamoMapToObject';
2
3
  export * from './deleteItem';
3
4
  export * from './getAllItems';
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./batchPutItems"), exports);
17
18
  __exportStar(require("./convertDynamoMapToObject"), exports);
18
19
  __exportStar(require("./deleteItem"), exports);
19
20
  __exportStar(require("./getAllItems"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA2C;AAC3C,+CAA6B;AAC7B,gDAA8B;AAC9B,4CAA0B;AAC1B,4CAA0B;AAC1B,0CAAwB;AACxB,yCAAuB;AACvB,+CAA6B","sourcesContent":["export * from './convertDynamoMapToObject';\nexport * from './deleteItem';\nexport * from './getAllItems';\nexport * from './getItem';\nexport * from './putItem';\nexport * from './query';\nexport * from './scan';\nexport * from './updateItem';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC;AAChC,6DAA2C;AAC3C,+CAA6B;AAC7B,gDAA8B;AAC9B,4CAA0B;AAC1B,4CAA0B;AAC1B,0CAAwB;AACxB,yCAAuB;AACvB,+CAA6B","sourcesContent":["export * from './batchPutItems';\nexport * from './convertDynamoMapToObject';\nexport * from './deleteItem';\nexport * from './getAllItems';\nexport * from './getItem';\nexport * from './putItem';\nexport * from './query';\nexport * from './scan';\nexport * from './updateItem';\n"]}
@@ -13,16 +13,10 @@ exports.putItem = putItem;
13
13
  const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
14
14
  const createAwsClient_1 = require("../createAwsClient");
15
15
  const qpqDynamoOrm_1 = require("./qpqDynamoOrm");
16
- // Root items must be objects. A root-level list is technically storable, but
17
- // DynamoDB cannot conditionally update or delete individual list elements,
18
- // and a root list is usually better modelled as its own table.
19
- const convertObjectToDynamoItem = (obj) => {
20
- return (0, qpqDynamoOrm_1.buildAttributeValue)(obj).M;
21
- };
22
16
  function putItem(tableName, item, attributes, options, region) {
23
17
  return __awaiter(this, void 0, void 0, function* () {
24
18
  const dynamoDBClient = (0, createAwsClient_1.createAwsClient)(client_dynamodb_1.DynamoDBClient, { region });
25
- yield dynamoDBClient.send(new client_dynamodb_1.PutItemCommand(Object.assign({ TableName: tableName, Item: convertObjectToDynamoItem(item) }, (options.ifNotExistsAttribute
19
+ yield dynamoDBClient.send(new client_dynamodb_1.PutItemCommand(Object.assign({ TableName: tableName, Item: (0, qpqDynamoOrm_1.convertObjectToDynamoItem)(item) }, (options.ifNotExistsAttribute
26
20
  ? {
27
21
  ConditionExpression: 'attribute_not_exists(#ineAttr)',
28
22
  ExpressionAttributeNames: { '#ineAttr': options.ifNotExistsAttribute },
@@ -1 +1 @@
1
- {"version":3,"file":"putItem.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/putItem.ts"],"names":[],"mappings":";;;;;;;;;;;AA6BA,0BAqBC;AAhDD,8DAA0F;AAE1F,wDAAqD;AACrD,iDAAqD;AAiBrD,6EAA6E;AAC7E,2EAA2E;AAC3E,+DAA+D;AAC/D,MAAM,yBAAyB,GAAG,CAAC,GAAsB,EAAkC,EAAE;IAC3F,OAAO,IAAA,kCAAmB,EAAC,GAAG,CAAC,CAAC,CAAE,CAAC;AACrC,CAAC,CAAC;AAEF,SAAsB,OAAO,CAC3B,SAAiB,EACjB,IAAuB,EACvB,UAAoB,EACpB,OAAuB,EACvB,MAAc;;QAEd,MAAM,cAAc,GAAG,IAAA,iCAAe,EAAC,gCAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAEnE,MAAM,cAAc,CAAC,IAAI,CACvB,IAAI,gCAAc,iBAChB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC,IAClC,CAAC,OAAO,CAAC,oBAAoB;YAC9B,CAAC,CAAC;gBACE,mBAAmB,EAAE,gCAAgC;gBACrD,wBAAwB,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;aACvE;YACH,CAAC,CAAC,EAAE,CAAC,EACP,CACH,CAAC;IACJ,CAAC;CAAA","sourcesContent":["import { KvsKey, KvsObjectDataType } from 'quidproquo-core';\n\nimport { AttributeValue, DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';\n\nimport { createAwsClient } from '../createAwsClient';\nimport { buildAttributeValue } from './qpqDynamoOrm';\n\nexport type PutItemOptions = {\n // Requested TTL in seconds. Not currently applied: the table's ttlAttribute\n // name is not plumbed through to this call, so the value is silently\n // dropped. Wire storeConfig.ttlAttribute through and write the epoch expiry\n // before relying on it.\n expires?: number;\n\n // When set, the put becomes a conditional insert: it fails with\n // ConditionalCheckFailedException if an item with the same primary key\n // already exists. The value is the attribute name to condition on (any\n // key attribute works: the condition evaluates against the item at the\n // full primary key).\n ifNotExistsAttribute?: string;\n};\n\n// Root items must be objects. A root-level list is technically storable, but\n// DynamoDB cannot conditionally update or delete individual list elements,\n// and a root list is usually better modelled as its own table.\nconst convertObjectToDynamoItem = (obj: KvsObjectDataType): Record<string, AttributeValue> => {\n return buildAttributeValue(obj).M!;\n};\n\nexport async function putItem(\n tableName: string,\n item: KvsObjectDataType,\n attributes: KvsKey[],\n options: PutItemOptions,\n region: string,\n): Promise<void> {\n const dynamoDBClient = createAwsClient(DynamoDBClient, { region });\n\n await dynamoDBClient.send(\n new PutItemCommand({\n TableName: tableName,\n Item: convertObjectToDynamoItem(item),\n ...(options.ifNotExistsAttribute\n ? {\n ConditionExpression: 'attribute_not_exists(#ineAttr)',\n ExpressionAttributeNames: { '#ineAttr': options.ifNotExistsAttribute },\n }\n : {}),\n }),\n );\n}\n"]}
1
+ {"version":3,"file":"putItem.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/putItem.ts"],"names":[],"mappings":";;;;;;;;;;;AAsBA,0BAqBC;AAzCD,8DAA0E;AAE1E,wDAAqD;AACrD,iDAA2D;AAiB3D,SAAsB,OAAO,CAC3B,SAAiB,EACjB,IAAuB,EACvB,UAAoB,EACpB,OAAuB,EACvB,MAAc;;QAEd,MAAM,cAAc,GAAG,IAAA,iCAAe,EAAC,gCAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAEnE,MAAM,cAAc,CAAC,IAAI,CACvB,IAAI,gCAAc,iBAChB,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,IAAA,wCAAyB,EAAC,IAAI,CAAC,IAClC,CAAC,OAAO,CAAC,oBAAoB;YAC9B,CAAC,CAAC;gBACE,mBAAmB,EAAE,gCAAgC;gBACrD,wBAAwB,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;aACvE;YACH,CAAC,CAAC,EAAE,CAAC,EACP,CACH,CAAC;IACJ,CAAC;CAAA","sourcesContent":["import { KvsKey, KvsObjectDataType } from 'quidproquo-core';\n\nimport { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';\n\nimport { createAwsClient } from '../createAwsClient';\nimport { convertObjectToDynamoItem } from './qpqDynamoOrm';\n\nexport type PutItemOptions = {\n // Requested TTL in seconds. Not currently applied: the table's ttlAttribute\n // name is not plumbed through to this call, so the value is silently\n // dropped. Wire storeConfig.ttlAttribute through and write the epoch expiry\n // before relying on it.\n expires?: number;\n\n // When set, the put becomes a conditional insert: it fails with\n // ConditionalCheckFailedException if an item with the same primary key\n // already exists. The value is the attribute name to condition on (any\n // key attribute works: the condition evaluates against the item at the\n // full primary key).\n ifNotExistsAttribute?: string;\n};\n\nexport async function putItem(\n tableName: string,\n item: KvsObjectDataType,\n attributes: KvsKey[],\n options: PutItemOptions,\n region: string,\n): Promise<void> {\n const dynamoDBClient = createAwsClient(DynamoDBClient, { region });\n\n await dynamoDBClient.send(\n new PutItemCommand({\n TableName: tableName,\n Item: convertObjectToDynamoItem(item),\n ...(options.ifNotExistsAttribute\n ? {\n ConditionExpression: 'attribute_not_exists(#ineAttr)',\n ExpressionAttributeNames: { '#ineAttr': options.ifNotExistsAttribute },\n }\n : {}),\n }),\n );\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { KvsObjectDataType } from 'quidproquo-core';
2
+ import { AttributeValue } from '@aws-sdk/client-dynamodb';
3
+ export declare const convertObjectToDynamoItem: (obj: KvsObjectDataType) => Record<string, AttributeValue>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertObjectToDynamoItem = void 0;
4
+ const buildAttributeValue_1 = require("./buildAttributeValue");
5
+ // Root items must be objects. A root-level list is technically storable, but
6
+ // DynamoDB cannot conditionally update or delete individual list elements,
7
+ // and a root list is usually better modelled as its own table.
8
+ const convertObjectToDynamoItem = (obj) => {
9
+ return (0, buildAttributeValue_1.buildAttributeValue)(obj).M;
10
+ };
11
+ exports.convertObjectToDynamoItem = convertObjectToDynamoItem;
12
+ //# sourceMappingURL=convertObjectToDynamoItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convertObjectToDynamoItem.js","sourceRoot":"","sources":["../../../../../src/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.ts"],"names":[],"mappings":";;;AAIA,+DAA4D;AAE5D,6EAA6E;AAC7E,2EAA2E;AAC3E,+DAA+D;AACxD,MAAM,yBAAyB,GAAG,CAAC,GAAsB,EAAkC,EAAE;IAClG,OAAO,IAAA,yCAAmB,EAAC,GAAG,CAAC,CAAC,CAAE,CAAC;AACrC,CAAC,CAAC;AAFW,QAAA,yBAAyB,6BAEpC","sourcesContent":["import { KvsObjectDataType } from 'quidproquo-core';\n\nimport { AttributeValue } from '@aws-sdk/client-dynamodb';\n\nimport { buildAttributeValue } from './buildAttributeValue';\n\n// Root items must be objects. A root-level list is technically storable, but\n// DynamoDB cannot conditionally update or delete individual list elements,\n// and a root list is usually better modelled as its own table.\nexport const convertObjectToDynamoItem = (obj: KvsObjectDataType): Record<string, AttributeValue> => {\n return buildAttributeValue(obj).M!;\n};\n"]}
@@ -1,4 +1,5 @@
1
1
  export * from './buildAttributeValue';
2
+ export * from './convertObjectToDynamoItem';
2
3
  export * from './buildDynamoQueryExpression';
3
4
  export * from './buildDynamoUpdateExpression';
4
5
  export * from './buildExpressionAttributeNames';
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./buildAttributeValue"), exports);
18
+ __exportStar(require("./convertObjectToDynamoItem"), exports);
18
19
  __exportStar(require("./buildDynamoQueryExpression"), exports);
19
20
  __exportStar(require("./buildDynamoUpdateExpression"), exports);
20
21
  __exportStar(require("./buildExpressionAttributeNames"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/logic/dynamo/qpqDynamoOrm/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAsC;AACtC,+DAA6C;AAC7C,gEAA8C;AAC9C,kEAAgD;AAChD,mEAAiD;AACjD,wEAAsD;AACtD,yEAAuD;AACvD,8DAA4C;AAC5C,wEAAsD;AACtD,4CAA0B;AAC1B,gDAA8B;AAC9B,iDAA+B;AAC/B,yDAAuC;AACvC,wDAAsC","sourcesContent":["export * from './buildAttributeValue';\nexport * from './buildDynamoQueryExpression';\nexport * from './buildDynamoUpdateExpression';\nexport * from './buildExpressionAttributeNames';\nexport * from './buildExpressionAttributeValues';\nexport * from './buildUpdateExpressionAttributeNames';\nexport * from './buildUpdateExpressionAttributeValues';\nexport * from './flattenKvsQueryConditions';\nexport * from './getDynamoTableIndexByConfigAndQuery';\nexport * from './getHash';\nexport * from './getItemName';\nexport * from './getValueName';\nexport * from './isKvsLogicalOperator';\nexport * from './isKvsQueryCondition';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/logic/dynamo/qpqDynamoOrm/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAsC;AACtC,8DAA4C;AAC5C,+DAA6C;AAC7C,gEAA8C;AAC9C,kEAAgD;AAChD,mEAAiD;AACjD,wEAAsD;AACtD,yEAAuD;AACvD,8DAA4C;AAC5C,wEAAsD;AACtD,4CAA0B;AAC1B,gDAA8B;AAC9B,iDAA+B;AAC/B,yDAAuC;AACvC,wDAAsC","sourcesContent":["export * from './buildAttributeValue';\nexport * from './convertObjectToDynamoItem';\nexport * from './buildDynamoQueryExpression';\nexport * from './buildDynamoUpdateExpression';\nexport * from './buildExpressionAttributeNames';\nexport * from './buildExpressionAttributeValues';\nexport * from './buildUpdateExpressionAttributeNames';\nexport * from './buildUpdateExpressionAttributeValues';\nexport * from './flattenKvsQueryConditions';\nexport * from './getDynamoTableIndexByConfigAndQuery';\nexport * from './getHash';\nexport * from './getItemName';\nexport * from './getValueName';\nexport * from './isKvsLogicalOperator';\nexport * from './isKvsQueryCondition';\n"]}
@@ -0,0 +1,2 @@
1
+ import { ActionProcessorListResolver } from 'quidproquo-core';
2
+ export declare const getKeyValueStoreUpsertManyActionProcessor: ActionProcessorListResolver;
@@ -0,0 +1,49 @@
1
+ import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
2
+ import { actionResult, actionResultError, actionResultErrorFromCaughtError, getScopedKvsTranslatorOrThrow, KeyValueStoreActionType, KeyValueStoreUpsertManyErrorTypeEnum, resolveKvsStoreConfigOrThrow, } from 'quidproquo-core';
3
+ import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
4
+ import { batchPutItems } from '../../../logic/dynamo';
5
+ // The batch sibling of Upsert: BatchWriteItem in chunks of 25, partial-acceptance
6
+ // retry inside batchPutItems. Streams emit one record per item no matter how it
7
+ // was written, so downstream projectors see the same records as N single upserts.
8
+ // Unconditional (no ifNotExists — BatchWriteItem carries no conditions).
9
+ const getProcessKeyValueStoreUpsertMany = (qpqConfig) => {
10
+ return async ({ keyValueStoreName, items, options }) => {
11
+ const dynamoTableName = getKvsDynamoTableNameFromConfig(keyValueStoreName, qpqConfig, 'kvs');
12
+ const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
13
+ try {
14
+ const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);
15
+ // Same-key duplicates are rejected UP FRONT, before anything is written.
16
+ // Dynamo itself only rejects them when both land in the same 25-item
17
+ // chunk — across chunks the later write silently wins — so left to the
18
+ // service the outcome would depend on item position.
19
+ const seenKeys = new Set();
20
+ for (const item of items) {
21
+ const itemKey = [storeConfig.partitionKey, ...storeConfig.sortKeys].map((key) => String(item[key.key])).join('#');
22
+ if (seenKeys.has(itemKey)) {
23
+ return actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.DuplicateKey, `Duplicate key [${itemKey}] in batch upsert to [${keyValueStoreName}]`);
24
+ }
25
+ seenKeys.add(itemKey);
26
+ }
27
+ // The scope lives inside the stored partition key value, so each item is
28
+ // persisted with a composed pk; reads strip it back off.
29
+ const scoped = getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, options?.scope);
30
+ await batchPutItems(dynamoTableName, items.map((item) => scoped.item(item)), region);
31
+ return actionResult(void 0);
32
+ }
33
+ catch (error) {
34
+ return actionResultErrorFromCaughtError(error, {
35
+ InternalServerError: () => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS Service Unavailable'),
36
+ ResourceNotFoundException: () => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ResourceNotFound, 'KVS Resource Not Found'),
37
+ // Retries exhausted on UnprocessedItems — sustained throttle, a transient
38
+ // fault: callers must see ServiceUnavailable, not a permanent failure.
39
+ BatchWriteUnprocessedItemsError: () => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS batch write throttled'),
40
+ InvalidScopeError: (error) => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.InvalidScope, error.message),
41
+ KvsStoreNotFoundError: (error) => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.StoreNotFound, error.message),
42
+ });
43
+ }
44
+ };
45
+ };
46
+ export const getKeyValueStoreUpsertManyActionProcessor = async (qpqConfig) => ({
47
+ [KeyValueStoreActionType.UpsertMany]: getProcessKeyValueStoreUpsertMany(qpqConfig),
48
+ });
49
+ //# sourceMappingURL=getKeyValueStoreUpsertManyActionProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getKeyValueStoreUpsertManyActionProcessor.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport {\n ActionProcessorList,\n ActionProcessorListResolver,\n actionResult,\n actionResultError,\n actionResultErrorFromCaughtError,\n getScopedKvsTranslatorOrThrow,\n KeyValueStoreActionType,\n KeyValueStoreUpsertManyActionProcessor,\n KeyValueStoreUpsertManyErrorTypeEnum,\n QPQConfig,\n resolveKvsStoreConfigOrThrow,\n} from 'quidproquo-core';\n\nimport { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';\nimport { batchPutItems } from '../../../logic/dynamo';\n\n// The batch sibling of Upsert: BatchWriteItem in chunks of 25, partial-acceptance\n// retry inside batchPutItems. Streams emit one record per item no matter how it\n// was written, so downstream projectors see the same records as N single upserts.\n// Unconditional (no ifNotExists — BatchWriteItem carries no conditions).\nconst getProcessKeyValueStoreUpsertMany = (qpqConfig: QPQConfig): KeyValueStoreUpsertManyActionProcessor<any> => {\n return async ({ keyValueStoreName, items, options }) => {\n const dynamoTableName = getKvsDynamoTableNameFromConfig(keyValueStoreName, qpqConfig, 'kvs');\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n\n try {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n\n // Same-key duplicates are rejected UP FRONT, before anything is written.\n // Dynamo itself only rejects them when both land in the same 25-item\n // chunk — across chunks the later write silently wins — so left to the\n // service the outcome would depend on item position.\n const seenKeys = new Set<string>();\n for (const item of items) {\n const itemKey = [storeConfig.partitionKey, ...storeConfig.sortKeys].map((key) => String(item[key.key])).join('#');\n if (seenKeys.has(itemKey)) {\n return actionResultError(\n KeyValueStoreUpsertManyErrorTypeEnum.DuplicateKey,\n `Duplicate key [${itemKey}] in batch upsert to [${keyValueStoreName}]`,\n );\n }\n seenKeys.add(itemKey);\n }\n\n // The scope lives inside the stored partition key value, so each item is\n // persisted with a composed pk; reads strip it back off.\n const scoped = getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, options?.scope);\n\n await batchPutItems(\n dynamoTableName,\n items.map((item) => scoped.item(item)),\n region,\n );\n\n return actionResult(void 0);\n } catch (error: unknown) {\n return actionResultErrorFromCaughtError(error, {\n InternalServerError: () => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS Service Unavailable'),\n ResourceNotFoundException: () => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ResourceNotFound, 'KVS Resource Not Found'),\n // Retries exhausted on UnprocessedItems — sustained throttle, a transient\n // fault: callers must see ServiceUnavailable, not a permanent failure.\n BatchWriteUnprocessedItemsError: () =>\n actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.ServiceUnavailable, 'KVS batch write throttled'),\n InvalidScopeError: (error) => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.InvalidScope, error.message),\n KvsStoreNotFoundError: (error) => actionResultError(KeyValueStoreUpsertManyErrorTypeEnum.StoreNotFound, error.message),\n });\n }\n };\n};\n\nexport const getKeyValueStoreUpsertManyActionProcessor: ActionProcessorListResolver = async (qpqConfig: QPQConfig): Promise<ActionProcessorList> => ({\n [KeyValueStoreActionType.UpsertMany]: getProcessKeyValueStoreUpsertMany(qpqConfig),\n});\n"]}
@@ -6,11 +6,13 @@ import { getKeyValueStoreScanActionProcessor } from './getKeyValueStoreScanActio
6
6
  import { getKeyValueStoreScanAllScopesActionProcessor } from './getKeyValueStoreScanAllScopesActionProcessor';
7
7
  import { getKeyValueStoreUpdateActionProcessor } from './getKeyValueStoreUpdateActionProcessor';
8
8
  import { getKeyValueStoreUpsertActionProcessor } from './getKeyValueStoreUpsertActionProcessor';
9
+ import { getKeyValueStoreUpsertManyActionProcessor } from './getKeyValueStoreUpsertManyActionProcessor';
9
10
  export const getKeyValueStoreActionProcessor = async (qpqConfig, dynamicModuleLoader) => ({
10
11
  ...(await getKeyValueStoreDeleteActionProcessor(qpqConfig, dynamicModuleLoader)),
11
12
  ...(await getKeyValueStoreGetActionProcessor(qpqConfig, dynamicModuleLoader)),
12
13
  ...(await getKeyValueStoreGetAllActionProcessor(qpqConfig, dynamicModuleLoader)),
13
14
  ...(await getKeyValueStoreUpsertActionProcessor(qpqConfig, dynamicModuleLoader)),
15
+ ...(await getKeyValueStoreUpsertManyActionProcessor(qpqConfig, dynamicModuleLoader)),
14
16
  ...(await getKeyValueStoreUpdateActionProcessor(qpqConfig, dynamicModuleLoader)),
15
17
  ...(await getKeyValueStoreQueryActionProcessor(qpqConfig, dynamicModuleLoader)),
16
18
  ...(await getKeyValueStoreScanActionProcessor(qpqConfig, dynamicModuleLoader)),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EAAE,kCAAkC,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,mCAAmC,EAAE,MAAM,uCAAuC,CAAC;AAC5F,OAAO,EAAE,4CAA4C,EAAE,MAAM,gDAAgD,CAAC;AAC9G,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAEhG,MAAM,CAAC,MAAM,+BAA+B,GAAgC,KAAK,EAC/E,SAAoB,EACpB,mBAAwC,EACV,EAAE,CAAC,CAAC;IAClC,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,kCAAkC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAC7E,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,oCAAoC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAC/E,GAAG,CAAC,MAAM,mCAAmC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAC9E,GAAG,CAAC,MAAM,4CAA4C,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;CACxF,CAAC,CAAC","sourcesContent":["import { ActionProcessorList, ActionProcessorListResolver, DynamicModuleLoader, QPQConfig } from 'quidproquo-core';\n\nimport { getKeyValueStoreDeleteActionProcessor } from './getKeyValueStoreDeleteActionProcessor';\nimport { getKeyValueStoreGetActionProcessor } from './getKeyValueStoreGetActionProcessor';\nimport { getKeyValueStoreGetAllActionProcessor } from './getKeyValueStoreGetAllActionProcessor';\nimport { getKeyValueStoreQueryActionProcessor } from './getKeyValueStoreQueryActionProcessor';\nimport { getKeyValueStoreScanActionProcessor } from './getKeyValueStoreScanActionProcessor';\nimport { getKeyValueStoreScanAllScopesActionProcessor } from './getKeyValueStoreScanAllScopesActionProcessor';\nimport { getKeyValueStoreUpdateActionProcessor } from './getKeyValueStoreUpdateActionProcessor';\nimport { getKeyValueStoreUpsertActionProcessor } from './getKeyValueStoreUpsertActionProcessor';\n\nexport const getKeyValueStoreActionProcessor: ActionProcessorListResolver = async (\n qpqConfig: QPQConfig,\n dynamicModuleLoader: DynamicModuleLoader,\n): Promise<ActionProcessorList> => ({\n ...(await getKeyValueStoreDeleteActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetAllActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpsertActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpdateActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreQueryActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanAllScopesActionProcessor(qpqConfig, dynamicModuleLoader)),\n});\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EAAE,kCAAkC,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,mCAAmC,EAAE,MAAM,uCAAuC,CAAC;AAC5F,OAAO,EAAE,4CAA4C,EAAE,MAAM,gDAAgD,CAAC;AAC9G,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EAAE,qCAAqC,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EAAE,yCAAyC,EAAE,MAAM,6CAA6C,CAAC;AAExG,MAAM,CAAC,MAAM,+BAA+B,GAAgC,KAAK,EAC/E,SAAoB,EACpB,mBAAwC,EACV,EAAE,CAAC,CAAC;IAClC,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,kCAAkC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAC7E,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,yCAAyC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IACpF,GAAG,CAAC,MAAM,qCAAqC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAChF,GAAG,CAAC,MAAM,oCAAoC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAC/E,GAAG,CAAC,MAAM,mCAAmC,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAC9E,GAAG,CAAC,MAAM,4CAA4C,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;CACxF,CAAC,CAAC","sourcesContent":["import { ActionProcessorList, ActionProcessorListResolver, DynamicModuleLoader, QPQConfig } from 'quidproquo-core';\n\nimport { getKeyValueStoreDeleteActionProcessor } from './getKeyValueStoreDeleteActionProcessor';\nimport { getKeyValueStoreGetActionProcessor } from './getKeyValueStoreGetActionProcessor';\nimport { getKeyValueStoreGetAllActionProcessor } from './getKeyValueStoreGetAllActionProcessor';\nimport { getKeyValueStoreQueryActionProcessor } from './getKeyValueStoreQueryActionProcessor';\nimport { getKeyValueStoreScanActionProcessor } from './getKeyValueStoreScanActionProcessor';\nimport { getKeyValueStoreScanAllScopesActionProcessor } from './getKeyValueStoreScanAllScopesActionProcessor';\nimport { getKeyValueStoreUpdateActionProcessor } from './getKeyValueStoreUpdateActionProcessor';\nimport { getKeyValueStoreUpsertActionProcessor } from './getKeyValueStoreUpsertActionProcessor';\nimport { getKeyValueStoreUpsertManyActionProcessor } from './getKeyValueStoreUpsertManyActionProcessor';\n\nexport const getKeyValueStoreActionProcessor: ActionProcessorListResolver = async (\n qpqConfig: QPQConfig,\n dynamicModuleLoader: DynamicModuleLoader,\n): Promise<ActionProcessorList> => ({\n ...(await getKeyValueStoreDeleteActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreGetAllActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpsertActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpsertManyActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreUpdateActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreQueryActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanActionProcessor(qpqConfig, dynamicModuleLoader)),\n ...(await getKeyValueStoreScanAllScopesActionProcessor(qpqConfig, dynamicModuleLoader)),\n});\n"]}
@@ -0,0 +1,5 @@
1
+ import { KvsObjectDataType } from 'quidproquo-core';
2
+ export declare class BatchWriteUnprocessedItemsError extends Error {
3
+ constructor(tableName: string, unprocessedCount: number, attempts: number);
4
+ }
5
+ export declare function batchPutItems(tableName: string, items: KvsObjectDataType[], region: string): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import { BatchWriteItemCommand, DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import { createAwsClient } from '../createAwsClient';
3
+ import { convertObjectToDynamoItem } from './qpqDynamoOrm';
4
+ // BatchWriteItem accepts at most 25 writes per call.
5
+ const BATCH_WRITE_MAX_ITEMS = 25;
6
+ // UnprocessedItems is DynamoDB's throttle back-pressure, not an error: the
7
+ // service accepted part of the batch and hands the rest back for resubmission.
8
+ // Retry with backoff; only a batch that STAYS unprocessed is a real failure.
9
+ const MAX_UNPROCESSED_RETRIES = 8;
10
+ const RETRY_BASE_DELAY_MS = 50;
11
+ // Errors are matched by NAME in the processors' error tables, so the name is
12
+ // the API. Exhausted retries mean sustained throttle — a transient fault the
13
+ // processor must classify as ServiceUnavailable, never a generic (permanent)
14
+ // failure.
15
+ export class BatchWriteUnprocessedItemsError extends Error {
16
+ constructor(tableName, unprocessedCount, attempts) {
17
+ super(`BatchWriteItem to [${tableName}] still has ${unprocessedCount} unprocessed items after ${attempts} attempts`);
18
+ this.name = 'BatchWriteUnprocessedItemsError';
19
+ }
20
+ }
21
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
22
+ // Unconditional batched puts. No ConditionExpression exists on BatchWriteItem,
23
+ // so unlike putItem there is no ifNotExists — callers must only batch items
24
+ // whose keys are unique by construction (see askKeyValueStoreUpsertMany).
25
+ export async function batchPutItems(tableName, items, region) {
26
+ const dynamoDBClient = createAwsClient(DynamoDBClient, { region });
27
+ for (let offset = 0; offset < items.length; offset += BATCH_WRITE_MAX_ITEMS) {
28
+ let writeRequests = items
29
+ .slice(offset, offset + BATCH_WRITE_MAX_ITEMS)
30
+ .map((item) => ({ PutRequest: { Item: convertObjectToDynamoItem(item) } }));
31
+ for (let attempt = 0; writeRequests.length > 0; attempt += 1) {
32
+ if (attempt > MAX_UNPROCESSED_RETRIES) {
33
+ throw new BatchWriteUnprocessedItemsError(tableName, writeRequests.length, attempt);
34
+ }
35
+ if (attempt > 0) {
36
+ await delay(RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));
37
+ }
38
+ const response = await dynamoDBClient.send(new BatchWriteItemCommand({
39
+ RequestItems: { [tableName]: writeRequests },
40
+ }));
41
+ writeRequests = response.UnprocessedItems?.[tableName] ?? [];
42
+ }
43
+ }
44
+ }
45
+ //# sourceMappingURL=batchPutItems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batchPutItems.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/batchPutItems.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAgB,MAAM,0BAA0B,CAAC;AAE/F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,qDAAqD;AACrD,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,2EAA2E;AAC3E,+EAA+E;AAC/E,6EAA6E;AAC7E,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAClC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,WAAW;AACX,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IACxD,YAAY,SAAiB,EAAE,gBAAwB,EAAE,QAAgB;QACvE,KAAK,CAAC,sBAAsB,SAAS,eAAe,gBAAgB,4BAA4B,QAAQ,WAAW,CAAC,CAAC;QACrH,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;IAChD,CAAC;CACF;AAED,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEhF,+EAA+E;AAC/E,4EAA4E;AAC5E,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,KAA0B,EAAE,MAAc;IAC/F,MAAM,cAAc,GAAG,eAAe,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAEnE,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,qBAAqB,EAAE,CAAC;QAC5E,IAAI,aAAa,GAAmB,KAAK;aACtC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAAC;aAC7C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAE9E,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;YAC7D,IAAI,OAAO,GAAG,uBAAuB,EAAE,CAAC;gBACtC,MAAM,IAAI,+BAA+B,CAAC,SAAS,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACtF,CAAC;YAED,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBAChB,MAAM,KAAK,CAAC,mBAAmB,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YACxD,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,CACxC,IAAI,qBAAqB,CAAC;gBACxB,YAAY,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE;aAC7C,CAAC,CACH,CAAC;YAEF,aAAa,GAAG,QAAQ,CAAC,gBAAgB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAC/D,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { KvsObjectDataType } from 'quidproquo-core';\n\nimport { BatchWriteItemCommand, DynamoDBClient, WriteRequest } from '@aws-sdk/client-dynamodb';\n\nimport { createAwsClient } from '../createAwsClient';\nimport { convertObjectToDynamoItem } from './qpqDynamoOrm';\n\n// BatchWriteItem accepts at most 25 writes per call.\nconst BATCH_WRITE_MAX_ITEMS = 25;\n\n// UnprocessedItems is DynamoDB's throttle back-pressure, not an error: the\n// service accepted part of the batch and hands the rest back for resubmission.\n// Retry with backoff; only a batch that STAYS unprocessed is a real failure.\nconst MAX_UNPROCESSED_RETRIES = 8;\nconst RETRY_BASE_DELAY_MS = 50;\n\n// Errors are matched by NAME in the processors' error tables, so the name is\n// the API. Exhausted retries mean sustained throttle — a transient fault the\n// processor must classify as ServiceUnavailable, never a generic (permanent)\n// failure.\nexport class BatchWriteUnprocessedItemsError extends Error {\n constructor(tableName: string, unprocessedCount: number, attempts: number) {\n super(`BatchWriteItem to [${tableName}] still has ${unprocessedCount} unprocessed items after ${attempts} attempts`);\n this.name = 'BatchWriteUnprocessedItemsError';\n }\n}\n\nconst delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n// Unconditional batched puts. No ConditionExpression exists on BatchWriteItem,\n// so unlike putItem there is no ifNotExists — callers must only batch items\n// whose keys are unique by construction (see askKeyValueStoreUpsertMany).\nexport async function batchPutItems(tableName: string, items: KvsObjectDataType[], region: string): Promise<void> {\n const dynamoDBClient = createAwsClient(DynamoDBClient, { region });\n\n for (let offset = 0; offset < items.length; offset += BATCH_WRITE_MAX_ITEMS) {\n let writeRequests: WriteRequest[] = items\n .slice(offset, offset + BATCH_WRITE_MAX_ITEMS)\n .map((item) => ({ PutRequest: { Item: convertObjectToDynamoItem(item) } }));\n\n for (let attempt = 0; writeRequests.length > 0; attempt += 1) {\n if (attempt > MAX_UNPROCESSED_RETRIES) {\n throw new BatchWriteUnprocessedItemsError(tableName, writeRequests.length, attempt);\n }\n\n if (attempt > 0) {\n await delay(RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));\n }\n\n const response = await dynamoDBClient.send(\n new BatchWriteItemCommand({\n RequestItems: { [tableName]: writeRequests },\n }),\n );\n\n writeRequests = response.UnprocessedItems?.[tableName] ?? [];\n }\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export * from './batchPutItems';
1
2
  export * from './convertDynamoMapToObject';
2
3
  export * from './deleteItem';
3
4
  export * from './getAllItems';
@@ -1,3 +1,4 @@
1
+ export * from './batchPutItems';
1
2
  export * from './convertDynamoMapToObject';
2
3
  export * from './deleteItem';
3
4
  export * from './getAllItems';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC","sourcesContent":["export * from './convertDynamoMapToObject';\nexport * from './deleteItem';\nexport * from './getAllItems';\nexport * from './getItem';\nexport * from './putItem';\nexport * from './query';\nexport * from './scan';\nexport * from './updateItem';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC","sourcesContent":["export * from './batchPutItems';\nexport * from './convertDynamoMapToObject';\nexport * from './deleteItem';\nexport * from './getAllItems';\nexport * from './getItem';\nexport * from './putItem';\nexport * from './query';\nexport * from './scan';\nexport * from './updateItem';\n"]}
@@ -1,12 +1,6 @@
1
1
  import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
2
2
  import { createAwsClient } from '../createAwsClient';
3
- import { buildAttributeValue } from './qpqDynamoOrm';
4
- // Root items must be objects. A root-level list is technically storable, but
5
- // DynamoDB cannot conditionally update or delete individual list elements,
6
- // and a root list is usually better modelled as its own table.
7
- const convertObjectToDynamoItem = (obj) => {
8
- return buildAttributeValue(obj).M;
9
- };
3
+ import { convertObjectToDynamoItem } from './qpqDynamoOrm';
10
4
  export async function putItem(tableName, item, attributes, options, region) {
11
5
  const dynamoDBClient = createAwsClient(DynamoDBClient, { region });
12
6
  await dynamoDBClient.send(new PutItemCommand({
@@ -1 +1 @@
1
- {"version":3,"file":"putItem.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/putItem.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAiBrD,6EAA6E;AAC7E,2EAA2E;AAC3E,+DAA+D;AAC/D,MAAM,yBAAyB,GAAG,CAAC,GAAsB,EAAkC,EAAE;IAC3F,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,IAAuB,EACvB,UAAoB,EACpB,OAAuB,EACvB,MAAc;IAEd,MAAM,cAAc,GAAG,eAAe,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAEnE,MAAM,cAAc,CAAC,IAAI,CACvB,IAAI,cAAc,CAAC;QACjB,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC;QACrC,GAAG,CAAC,OAAO,CAAC,oBAAoB;YAC9B,CAAC,CAAC;gBACE,mBAAmB,EAAE,gCAAgC;gBACrD,wBAAwB,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;aACvE;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["import { KvsKey, KvsObjectDataType } from 'quidproquo-core';\n\nimport { AttributeValue, DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';\n\nimport { createAwsClient } from '../createAwsClient';\nimport { buildAttributeValue } from './qpqDynamoOrm';\n\nexport type PutItemOptions = {\n // Requested TTL in seconds. Not currently applied: the table's ttlAttribute\n // name is not plumbed through to this call, so the value is silently\n // dropped. Wire storeConfig.ttlAttribute through and write the epoch expiry\n // before relying on it.\n expires?: number;\n\n // When set, the put becomes a conditional insert: it fails with\n // ConditionalCheckFailedException if an item with the same primary key\n // already exists. The value is the attribute name to condition on (any\n // key attribute works: the condition evaluates against the item at the\n // full primary key).\n ifNotExistsAttribute?: string;\n};\n\n// Root items must be objects. A root-level list is technically storable, but\n// DynamoDB cannot conditionally update or delete individual list elements,\n// and a root list is usually better modelled as its own table.\nconst convertObjectToDynamoItem = (obj: KvsObjectDataType): Record<string, AttributeValue> => {\n return buildAttributeValue(obj).M!;\n};\n\nexport async function putItem(\n tableName: string,\n item: KvsObjectDataType,\n attributes: KvsKey[],\n options: PutItemOptions,\n region: string,\n): Promise<void> {\n const dynamoDBClient = createAwsClient(DynamoDBClient, { region });\n\n await dynamoDBClient.send(\n new PutItemCommand({\n TableName: tableName,\n Item: convertObjectToDynamoItem(item),\n ...(options.ifNotExistsAttribute\n ? {\n ConditionExpression: 'attribute_not_exists(#ineAttr)',\n ExpressionAttributeNames: { '#ineAttr': options.ifNotExistsAttribute },\n }\n : {}),\n }),\n );\n}\n"]}
1
+ {"version":3,"file":"putItem.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/putItem.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAiB3D,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,IAAuB,EACvB,UAAoB,EACpB,OAAuB,EACvB,MAAc;IAEd,MAAM,cAAc,GAAG,eAAe,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAEnE,MAAM,cAAc,CAAC,IAAI,CACvB,IAAI,cAAc,CAAC;QACjB,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,yBAAyB,CAAC,IAAI,CAAC;QACrC,GAAG,CAAC,OAAO,CAAC,oBAAoB;YAC9B,CAAC,CAAC;gBACE,mBAAmB,EAAE,gCAAgC;gBACrD,wBAAwB,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,oBAAoB,EAAE;aACvE;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["import { KvsKey, KvsObjectDataType } from 'quidproquo-core';\n\nimport { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';\n\nimport { createAwsClient } from '../createAwsClient';\nimport { convertObjectToDynamoItem } from './qpqDynamoOrm';\n\nexport type PutItemOptions = {\n // Requested TTL in seconds. Not currently applied: the table's ttlAttribute\n // name is not plumbed through to this call, so the value is silently\n // dropped. Wire storeConfig.ttlAttribute through and write the epoch expiry\n // before relying on it.\n expires?: number;\n\n // When set, the put becomes a conditional insert: it fails with\n // ConditionalCheckFailedException if an item with the same primary key\n // already exists. The value is the attribute name to condition on (any\n // key attribute works: the condition evaluates against the item at the\n // full primary key).\n ifNotExistsAttribute?: string;\n};\n\nexport async function putItem(\n tableName: string,\n item: KvsObjectDataType,\n attributes: KvsKey[],\n options: PutItemOptions,\n region: string,\n): Promise<void> {\n const dynamoDBClient = createAwsClient(DynamoDBClient, { region });\n\n await dynamoDBClient.send(\n new PutItemCommand({\n TableName: tableName,\n Item: convertObjectToDynamoItem(item),\n ...(options.ifNotExistsAttribute\n ? {\n ConditionExpression: 'attribute_not_exists(#ineAttr)',\n ExpressionAttributeNames: { '#ineAttr': options.ifNotExistsAttribute },\n }\n : {}),\n }),\n );\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { KvsObjectDataType } from 'quidproquo-core';
2
+ import { AttributeValue } from '@aws-sdk/client-dynamodb';
3
+ export declare const convertObjectToDynamoItem: (obj: KvsObjectDataType) => Record<string, AttributeValue>;
@@ -0,0 +1,8 @@
1
+ import { buildAttributeValue } from './buildAttributeValue';
2
+ // Root items must be objects. A root-level list is technically storable, but
3
+ // DynamoDB cannot conditionally update or delete individual list elements,
4
+ // and a root list is usually better modelled as its own table.
5
+ export const convertObjectToDynamoItem = (obj) => {
6
+ return buildAttributeValue(obj).M;
7
+ };
8
+ //# sourceMappingURL=convertObjectToDynamoItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convertObjectToDynamoItem.js","sourceRoot":"","sources":["../../../../../src/logic/dynamo/qpqDynamoOrm/convertObjectToDynamoItem.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,6EAA6E;AAC7E,2EAA2E;AAC3E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAsB,EAAkC,EAAE;IAClG,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;AACrC,CAAC,CAAC","sourcesContent":["import { KvsObjectDataType } from 'quidproquo-core';\n\nimport { AttributeValue } from '@aws-sdk/client-dynamodb';\n\nimport { buildAttributeValue } from './buildAttributeValue';\n\n// Root items must be objects. A root-level list is technically storable, but\n// DynamoDB cannot conditionally update or delete individual list elements,\n// and a root list is usually better modelled as its own table.\nexport const convertObjectToDynamoItem = (obj: KvsObjectDataType): Record<string, AttributeValue> => {\n return buildAttributeValue(obj).M!;\n};\n"]}
@@ -1,4 +1,5 @@
1
1
  export * from './buildAttributeValue';
2
+ export * from './convertObjectToDynamoItem';
2
3
  export * from './buildDynamoQueryExpression';
3
4
  export * from './buildDynamoUpdateExpression';
4
5
  export * from './buildExpressionAttributeNames';
@@ -1,4 +1,5 @@
1
1
  export * from './buildAttributeValue';
2
+ export * from './convertObjectToDynamoItem';
2
3
  export * from './buildDynamoQueryExpression';
3
4
  export * from './buildDynamoUpdateExpression';
4
5
  export * from './buildExpressionAttributeNames';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/logic/dynamo/qpqDynamoOrm/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uCAAuC,CAAC;AACtD,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC","sourcesContent":["export * from './buildAttributeValue';\nexport * from './buildDynamoQueryExpression';\nexport * from './buildDynamoUpdateExpression';\nexport * from './buildExpressionAttributeNames';\nexport * from './buildExpressionAttributeValues';\nexport * from './buildUpdateExpressionAttributeNames';\nexport * from './buildUpdateExpressionAttributeValues';\nexport * from './flattenKvsQueryConditions';\nexport * from './getDynamoTableIndexByConfigAndQuery';\nexport * from './getHash';\nexport * from './getItemName';\nexport * from './getValueName';\nexport * from './isKvsLogicalOperator';\nexport * from './isKvsQueryCondition';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/logic/dynamo/qpqDynamoOrm/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uCAAuC,CAAC;AACtD,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC","sourcesContent":["export * from './buildAttributeValue';\nexport * from './convertObjectToDynamoItem';\nexport * from './buildDynamoQueryExpression';\nexport * from './buildDynamoUpdateExpression';\nexport * from './buildExpressionAttributeNames';\nexport * from './buildExpressionAttributeValues';\nexport * from './buildUpdateExpressionAttributeNames';\nexport * from './buildUpdateExpressionAttributeValues';\nexport * from './flattenKvsQueryConditions';\nexport * from './getDynamoTableIndexByConfigAndQuery';\nexport * from './getHash';\nexport * from './getItemName';\nexport * from './getValueName';\nexport * from './isKvsLogicalOperator';\nexport * from './isKvsQueryCondition';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quidproquo-actionprocessor-awslambda",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/esm/index.js",
@@ -63,11 +63,11 @@
63
63
  "lodash": "^4.17.21",
64
64
  "node-cache": "^5.1.2",
65
65
  "node-match-path": "^0.6.3",
66
- "quidproquo-actionprocessor-js": "0.1.14",
67
- "quidproquo-actionprocessor-node": "0.1.14",
68
- "quidproquo-config-aws": "0.1.14",
69
- "quidproquo-core": "0.1.14",
70
- "quidproquo-webserver": "0.1.14"
66
+ "quidproquo-actionprocessor-js": "0.1.16",
67
+ "quidproquo-actionprocessor-node": "0.1.16",
68
+ "quidproquo-config-aws": "0.1.16",
69
+ "quidproquo-core": "0.1.16",
70
+ "quidproquo-webserver": "0.1.16"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@types/busboy": "^1.5.0",
@@ -75,7 +75,7 @@
75
75
  "@types/lodash": "^4.14.194",
76
76
  "@types/node": "^22.13.13",
77
77
  "esbuild": "^0.25.8",
78
- "quidproquo-tsconfig": "0.1.14",
78
+ "quidproquo-tsconfig": "0.1.16",
79
79
  "typescript": "^5.8.2"
80
80
  }
81
81
  }