quidproquo-actionprocessor-awslambda 0.1.1 → 0.1.3

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 (104) hide show
  1. package/lib/commonjs/awsNamingUtils.d.ts +2 -0
  2. package/lib/commonjs/awsNamingUtils.js +24 -1
  3. package/lib/commonjs/getActionProcessor/core/ai/getAiPromptActionProcessor.js +11 -4
  4. package/lib/commonjs/getActionProcessor/core/ai/getAiPromptStreamActionProcessor.js +11 -4
  5. package/lib/commonjs/getActionProcessor/core/ai/logic/aiStreamPartMappers.js +8 -8
  6. package/lib/commonjs/getActionProcessor/core/ai/logic/createDriveFileResolver.d.ts +3 -0
  7. package/lib/commonjs/getActionProcessor/core/ai/logic/createDriveFileResolver.js +27 -0
  8. package/lib/commonjs/getActionProcessor/core/ai/logic/index.d.ts +1 -0
  9. package/lib/commonjs/getActionProcessor/core/ai/logic/index.js +1 -0
  10. package/lib/commonjs/getActionProcessor/core/ai/logic/toSdkMessages.d.ts +3 -2
  11. package/lib/commonjs/getActionProcessor/core/ai/logic/toSdkMessages.js +41 -24
  12. package/lib/commonjs/getActionProcessor/core/apiKeyValidation/getApiKeyValidationValidateActionProcessor.js +15 -11
  13. package/lib/commonjs/getActionProcessor/core/event/apiGatwayEvent/api/getEventGetRecordsActionProcessor.js +12 -1
  14. package/lib/commonjs/getActionProcessor/core/event/utils/parseMultipartFormData.d.ts +7 -1
  15. package/lib/commonjs/getActionProcessor/core/event/utils/parseMultipartFormData.js +77 -8
  16. package/lib/commonjs/getActionProcessor/core/eventBus/getEventBusSendMessagesActionProcessor.js +1 -1
  17. package/lib/commonjs/getActionProcessor/core/file/getFileStreamOpenActionProcessor.js +1 -3
  18. package/lib/commonjs/getActionProcessor/core/index.d.ts +1 -0
  19. package/lib/commonjs/getActionProcessor/core/index.js +3 -1
  20. package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js +2 -0
  21. package/lib/commonjs/getActionProcessor/core/metric/getMetricPutActionProcessor.d.ts +2 -0
  22. package/lib/commonjs/getActionProcessor/core/metric/getMetricPutActionProcessor.js +51 -0
  23. package/lib/commonjs/getActionProcessor/core/metric/index.d.ts +2 -0
  24. package/lib/commonjs/getActionProcessor/core/metric/index.js +17 -0
  25. package/lib/commonjs/lambdaHandlers/helpers/logger.js +1 -3
  26. package/lib/commonjs/lambdas/dynamicModuleLoader/dynamicModuleLoader.js +8 -1
  27. package/lib/commonjs/logic/apiGateway/getApiKeyValue.d.ts +1 -0
  28. package/lib/commonjs/logic/apiGateway/{getApiKeys.js → getApiKeyValue.js} +10 -14
  29. package/lib/commonjs/logic/cognito/utils/transformCognitoResponse.js +1 -1
  30. package/lib/commonjs/logic/dynamo/putItem.d.ts +1 -0
  31. package/lib/commonjs/logic/dynamo/putItem.js +6 -4
  32. package/lib/commonjs/logic/dynamo/qpqDynamoOrm/buildDynamoUpdate.js +1 -5
  33. package/lib/commonjs/logic/federatedModuleStore/FederatedModuleLoadError.d.ts +10 -0
  34. package/lib/commonjs/logic/federatedModuleStore/FederatedModuleLoadError.js +26 -0
  35. package/lib/commonjs/logic/federatedModuleStore/FederatedModuleStoreManifest.d.ts +8 -0
  36. package/lib/commonjs/logic/federatedModuleStore/FederatedModuleStoreManifest.js +2 -0
  37. package/lib/commonjs/logic/federatedModuleStore/index.d.ts +4 -0
  38. package/lib/commonjs/logic/federatedModuleStore/index.js +20 -0
  39. package/lib/commonjs/logic/federatedModuleStore/loadFederatedStory.d.ts +3 -0
  40. package/lib/commonjs/logic/federatedModuleStore/loadFederatedStory.js +295 -0
  41. package/lib/commonjs/logic/federatedModuleStore/sharedPackages.d.ts +1 -0
  42. package/lib/commonjs/logic/federatedModuleStore/sharedPackages.js +10 -0
  43. package/lib/commonjs/logic/index.d.ts +1 -0
  44. package/lib/commonjs/logic/index.js +1 -0
  45. package/lib/commonjs/logic/s3/generatePresignedUploadUrl.js +4 -4
  46. package/lib/esm/awsNamingUtils.d.ts +2 -0
  47. package/lib/esm/awsNamingUtils.js +20 -0
  48. package/lib/esm/getActionProcessor/core/ai/getAiPromptActionProcessor.js +11 -4
  49. package/lib/esm/getActionProcessor/core/ai/getAiPromptStreamActionProcessor.js +11 -4
  50. package/lib/esm/getActionProcessor/core/ai/logic/aiStreamPartMappers.js +8 -8
  51. package/lib/esm/getActionProcessor/core/ai/logic/createDriveFileResolver.d.ts +3 -0
  52. package/lib/esm/getActionProcessor/core/ai/logic/createDriveFileResolver.js +14 -0
  53. package/lib/esm/getActionProcessor/core/ai/logic/index.d.ts +1 -0
  54. package/lib/esm/getActionProcessor/core/ai/logic/index.js +1 -0
  55. package/lib/esm/getActionProcessor/core/ai/logic/toSdkMessages.d.ts +3 -2
  56. package/lib/esm/getActionProcessor/core/ai/logic/toSdkMessages.js +27 -20
  57. package/lib/esm/getActionProcessor/core/apiKeyValidation/getApiKeyValidationValidateActionProcessor.js +16 -12
  58. package/lib/esm/getActionProcessor/core/event/apiGatwayEvent/api/getEventGetRecordsActionProcessor.js +14 -3
  59. package/lib/esm/getActionProcessor/core/event/utils/parseMultipartFormData.d.ts +7 -1
  60. package/lib/esm/getActionProcessor/core/event/utils/parseMultipartFormData.js +75 -7
  61. package/lib/esm/getActionProcessor/core/eventBus/getEventBusSendMessagesActionProcessor.js +1 -1
  62. package/lib/esm/getActionProcessor/core/file/getFileDeleteActionProcessor.js +1 -1
  63. package/lib/esm/getActionProcessor/core/file/getFileGenerateTemporarySecureUrlActionProcessor.js +1 -1
  64. package/lib/esm/getActionProcessor/core/file/getFileGenerateTemporaryUploadSecureUrlActionProcessor.js +1 -1
  65. package/lib/esm/getActionProcessor/core/file/getFileStreamOpenActionProcessor.js +1 -3
  66. package/lib/esm/getActionProcessor/core/index.d.ts +1 -0
  67. package/lib/esm/getActionProcessor/core/index.js +3 -0
  68. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreDeleteActionProcessor.js +1 -1
  69. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreGetActionProcessor.js +1 -1
  70. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreGetAllActionProcessor.js +1 -1
  71. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreQueryActionProcessor.js +1 -1
  72. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreScanActionProcessor.js +1 -1
  73. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpdateActionProcessor.js +1 -1
  74. package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js +2 -0
  75. package/lib/esm/getActionProcessor/core/metric/getMetricPutActionProcessor.d.ts +2 -0
  76. package/lib/esm/getActionProcessor/core/metric/getMetricPutActionProcessor.js +43 -0
  77. package/lib/esm/getActionProcessor/core/metric/index.d.ts +2 -0
  78. package/lib/esm/getActionProcessor/core/metric/index.js +4 -0
  79. package/lib/esm/getActionProcessor/core/queue/getQueueSendMessageActionProcessor.js +1 -1
  80. package/lib/esm/lambdaHandlers/helpers/logger.js +1 -3
  81. package/lib/esm/lambdas/dynamicModuleLoader/dynamicModuleLoader.js +9 -2
  82. package/lib/esm/logic/apiGateway/getApiKeyValue.d.ts +1 -0
  83. package/lib/esm/logic/apiGateway/getApiKeyValue.js +12 -0
  84. package/lib/esm/logic/cognito/utils/transformCognitoResponse.js +2 -2
  85. package/lib/esm/logic/dynamo/putItem.d.ts +1 -0
  86. package/lib/esm/logic/dynamo/putItem.js +6 -0
  87. package/lib/esm/logic/dynamo/qpqDynamoOrm/buildDynamoUpdate.js +1 -5
  88. package/lib/esm/logic/federatedModuleStore/FederatedModuleLoadError.d.ts +10 -0
  89. package/lib/esm/logic/federatedModuleStore/FederatedModuleLoadError.js +23 -0
  90. package/lib/esm/logic/federatedModuleStore/FederatedModuleStoreManifest.d.ts +8 -0
  91. package/lib/esm/logic/federatedModuleStore/FederatedModuleStoreManifest.js +1 -0
  92. package/lib/esm/logic/federatedModuleStore/index.d.ts +4 -0
  93. package/lib/esm/logic/federatedModuleStore/index.js +4 -0
  94. package/lib/esm/logic/federatedModuleStore/loadFederatedStory.d.ts +3 -0
  95. package/lib/esm/logic/federatedModuleStore/loadFederatedStory.js +246 -0
  96. package/lib/esm/logic/federatedModuleStore/sharedPackages.d.ts +1 -0
  97. package/lib/esm/logic/federatedModuleStore/sharedPackages.js +7 -0
  98. package/lib/esm/logic/index.d.ts +1 -0
  99. package/lib/esm/logic/index.js +1 -0
  100. package/lib/esm/logic/s3/generatePresignedUploadUrl.js +5 -5
  101. package/package.json +8 -7
  102. package/lib/commonjs/logic/apiGateway/getApiKeys.d.ts +0 -2
  103. package/lib/esm/logic/apiGateway/getApiKeys.d.ts +0 -2
  104. package/lib/esm/logic/apiGateway/getApiKeys.js +0 -17
@@ -17,7 +17,9 @@ export declare const getCFExportNameApiKeyIdFromConfig: (apiKeyName: string, qpq
17
17
  export declare const getCFExportNameSnsTopicArnFromConfig: (eventBusName: string, qpqConfig: QPQConfig, applicationOverride?: string) => string;
18
18
  export declare const getCFExportNameDistributionIdArnFromConfig: (webEntryName: string, qpqConfig: QPQConfig, serviceOverride?: string, applicationOverride?: string) => string;
19
19
  export declare const getCFExportNameWebsocketApiIdFromConfig: (websocketApiName: string, qpqConfig: QPQConfig) => string;
20
+ export declare const getWebsocketApiIdSsmParameterName: (websocketApiName: string, qpqConfig: QPQConfig) => string;
20
21
  export declare const getEventBusSnsTopicArn: (eventBusName: string, qpqConfig: QPQConfig, module: string, environment: string, application: string, feature?: string) => string;
22
+ export declare const getAccountStackName: () => string;
21
23
  export declare const getBaseStackName: (qpqConfig: QPQConfig) => string;
22
24
  export declare const getInfStackName: (qpqConfig: QPQConfig) => string;
23
25
  export declare const getWebStackName: (qpqConfig: QPQConfig) => string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDomainStackName = exports.getBootstrapStackName = exports.getApiStackName = exports.getWebStackName = exports.getInfStackName = exports.getBaseStackName = exports.getEventBusSnsTopicArn = exports.getCFExportNameWebsocketApiIdFromConfig = exports.getCFExportNameDistributionIdArnFromConfig = exports.getCFExportNameSnsTopicArnFromConfig = exports.getCFExportNameApiKeyIdFromConfig = exports.getCFExportNameUserPoolClientIdFromConfig = exports.getCFExportNameCachePolicyIdFromConfig = exports.getCFExportNameUserPoolIdFromConfig = exports.getKvsDynamoTableNameFromConfig = exports.getQpqRuntimeResourceNameFromConfig = exports.getQpqRuntimeResourceName = exports.getConfigRuntimeResourceNameFromConfigWithServiceOverride = exports.getConfigRuntimeBootstrapResourceNameFromConfig = exports.resolveConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeBootstrapResourceName = exports.getConfigRuntimeResourceName = exports.getGlobalQpqRuntimeResourceName = exports.getGlobalConfigRuntimeResourceName = void 0;
3
+ exports.getDomainStackName = exports.getBootstrapStackName = exports.getApiStackName = exports.getWebStackName = exports.getInfStackName = exports.getBaseStackName = exports.getAccountStackName = exports.getEventBusSnsTopicArn = exports.getWebsocketApiIdSsmParameterName = exports.getCFExportNameWebsocketApiIdFromConfig = exports.getCFExportNameDistributionIdArnFromConfig = exports.getCFExportNameSnsTopicArnFromConfig = exports.getCFExportNameApiKeyIdFromConfig = exports.getCFExportNameUserPoolClientIdFromConfig = exports.getCFExportNameCachePolicyIdFromConfig = exports.getCFExportNameUserPoolIdFromConfig = exports.getKvsDynamoTableNameFromConfig = exports.getQpqRuntimeResourceNameFromConfig = exports.getQpqRuntimeResourceName = exports.getConfigRuntimeResourceNameFromConfigWithServiceOverride = exports.getConfigRuntimeBootstrapResourceNameFromConfig = exports.resolveConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeResourceNameFromConfig = exports.getConfigRuntimeBootstrapResourceName = exports.getConfigRuntimeResourceName = exports.getGlobalQpqRuntimeResourceName = exports.getGlobalConfigRuntimeResourceName = void 0;
4
4
  const quidproquo_config_aws_1 = require("quidproquo-config-aws");
5
5
  const quidproquo_core_1 = require("quidproquo-core");
6
6
  const quidproquo_webserver_1 = require("quidproquo-webserver");
@@ -156,6 +156,21 @@ const getCFExportNameWebsocketApiIdFromConfig = (websocketApiName, qpqConfig) =>
156
156
  return (0, exports.getQpqRuntimeResourceName)(resourceName, application, service, environment, feature, 'websocket-api-id-export');
157
157
  };
158
158
  exports.getCFExportNameWebsocketApiIdFromConfig = getCFExportNameWebsocketApiIdFromConfig;
159
+ // SSM parameter holding a websocket api's AWS-generated id, written by the owning service's
160
+ // inf stack. Referencing services resolve it at deploy time to build exact execute-api
161
+ // grants (deploy the owning inf stack first - same owner-publishes/others-read pattern as
162
+ // the domain certificate arn parameter).
163
+ const getWebsocketApiIdSsmParameterName = (websocketApiName, qpqConfig) => {
164
+ var _a, _b, _c, _d, _e;
165
+ const websocketApiConfig = quidproquo_webserver_1.qpqWebServerUtils.getWebsocketEntryByApiName(websocketApiName, qpqConfig);
166
+ const application = ((_a = websocketApiConfig.owner) === null || _a === void 0 ? void 0 : _a.application) || quidproquo_core_1.qpqCoreUtils.getApplicationName(qpqConfig);
167
+ const service = ((_b = websocketApiConfig.owner) === null || _b === void 0 ? void 0 : _b.module) || quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
168
+ const environment = ((_c = websocketApiConfig.owner) === null || _c === void 0 ? void 0 : _c.environment) || quidproquo_core_1.qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);
169
+ const feature = ((_d = websocketApiConfig.owner) === null || _d === void 0 ? void 0 : _d.feature) || quidproquo_core_1.qpqCoreUtils.getApplicationModuleFeature(qpqConfig);
170
+ const resourceName = ((_e = websocketApiConfig.owner) === null || _e === void 0 ? void 0 : _e.resourceNameOverride) || websocketApiName;
171
+ return `/qpq/websocket/api-id/${(0, exports.getConfigRuntimeResourceName)(resourceName, application, service, environment, feature)}`;
172
+ };
173
+ exports.getWebsocketApiIdSsmParameterName = getWebsocketApiIdSsmParameterName;
159
174
  const getEventBusSnsTopicArn = (eventBusName, qpqConfig, module, environment, application, feature) => {
160
175
  const topicName = (0, exports.getConfigRuntimeResourceName)(eventBusName, application, module, environment, feature);
161
176
  const accountInfo = (0, quidproquo_config_aws_1.getAwsServiceAccountInfoByDeploymentInfo)(qpqConfig, module, environment, feature, application);
@@ -164,6 +179,14 @@ const getEventBusSnsTopicArn = (eventBusName, qpqConfig, module, environment, ap
164
179
  return `arn:aws:sns:${region}:${awsAccountId}:${topicName}`;
165
180
  };
166
181
  exports.getEventBusSnsTopicArn = getEventBusSnsTopicArn;
182
+ // The account stack owns account-level resources (cloud trail, budgets, security services)
183
+ // so they never share an app's lifecycle. The name is deliberately static: stack names are
184
+ // already namespaced per account+region, and the resources it holds meter/audit the whole
185
+ // account - an app/environment segment would only fake a separation that doesn't exist.
186
+ // Everything in one account converges on this one stack, so exactly ONE repo/config must
187
+ // own it, and actor (feature) deploys must not deploy it.
188
+ const getAccountStackName = () => 'qpq-account';
189
+ exports.getAccountStackName = getAccountStackName;
167
190
  const getBaseStackName = (qpqConfig) => {
168
191
  const appName = quidproquo_core_1.qpqCoreUtils.getApplicationName(qpqConfig);
169
192
  const moduleName = quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
@@ -15,15 +15,22 @@ const ai_1 = require("ai");
15
15
  const logic_1 = require("./logic");
16
16
  const getProcessAiPrompt = (qpqConfig) => {
17
17
  return (payload, session, actionProcessorList, logger, updateSession, dynamicModuleLoader, streamRegistry) => __awaiter(void 0, void 0, void 0, function* () {
18
+ var _a;
18
19
  const prepared = (0, logic_1.prepareAiPromptCall)(qpqConfig, payload, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
19
20
  if ('error' in prepared) {
20
21
  return (0, quidproquo_core_1.actionResultError)(prepared.error.type, prepared.error.message);
21
22
  }
22
- const promptOrMessages = payload.messages
23
- ? { messages: (0, logic_1.toSdkMessages)(payload.messages) }
24
- : { prompt: payload.prompt };
25
23
  try {
26
- const result = yield (0, ai_1.generateText)(Object.assign(Object.assign({ model: prepared.model, system: payload.system }, promptOrMessages), { tools: prepared.tools, stopWhen: (0, ai_1.stepCountIs)(10) }));
24
+ const promptOrMessages = payload.messages
25
+ ? {
26
+ messages: yield (0, logic_1.toSdkMessages)(payload.messages, (0, logic_1.createDriveFileResolver)(qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry)),
27
+ }
28
+ : { prompt: payload.prompt };
29
+ // Extended thinking — the model reasons before answering, within the token budget.
30
+ const providerOptions = payload.reasoning
31
+ ? { bedrock: { reasoningConfig: { type: 'enabled', budgetTokens: (_a = payload.reasoning.budgetTokens) !== null && _a !== void 0 ? _a : 4096 } } }
32
+ : undefined;
33
+ const result = yield (0, ai_1.generateText)(Object.assign(Object.assign({ model: prepared.model, system: payload.system }, promptOrMessages), { tools: prepared.tools, providerOptions, stopWhen: (0, ai_1.stepCountIs)(10) }));
27
34
  return (0, quidproquo_core_1.actionResult)({ text: result.text });
28
35
  }
29
36
  catch (error) {
@@ -36,15 +36,22 @@ const awsLambdaUtils_1 = require("../../../awsLambdaUtils");
36
36
  const logic_1 = require("./logic");
37
37
  const getProcessAiPromptStream = (qpqConfig) => {
38
38
  return (payload, session, actionProcessorList, logger, updateSession, dynamicModuleLoader, streamRegistry) => __awaiter(void 0, void 0, void 0, function* () {
39
+ var _a;
39
40
  const prepared = (0, logic_1.prepareAiPromptCall)(qpqConfig, payload, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
40
41
  if ('error' in prepared) {
41
42
  return (0, quidproquo_core_1.actionResultError)(prepared.error.type, prepared.error.message);
42
43
  }
43
- const promptOrMessages = payload.messages
44
- ? { messages: (0, logic_1.toSdkMessages)(payload.messages) }
45
- : { prompt: payload.prompt };
46
44
  try {
47
- const { fullStream } = (0, ai_1.streamText)(Object.assign(Object.assign({ model: prepared.model, system: payload.system }, promptOrMessages), { tools: prepared.tools, stopWhen: (0, ai_1.stepCountIs)(10),
45
+ const promptOrMessages = payload.messages
46
+ ? {
47
+ messages: yield (0, logic_1.toSdkMessages)(payload.messages, (0, logic_1.createDriveFileResolver)(qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry)),
48
+ }
49
+ : { prompt: payload.prompt };
50
+ // Extended thinking — thinking progress streams out as Reasoning* parts.
51
+ const providerOptions = payload.reasoning
52
+ ? { bedrock: { reasoningConfig: { type: 'enabled', budgetTokens: (_a = payload.reasoning.budgetTokens) !== null && _a !== void 0 ? _a : 4096 } } }
53
+ : undefined;
54
+ const { fullStream } = (0, ai_1.streamText)(Object.assign(Object.assign({ model: prepared.model, system: payload.system }, promptOrMessages), { tools: prepared.tools, providerOptions, stopWhen: (0, ai_1.stepCountIs)(10),
48
55
  // streamText swallows errors by default to keep the server alive — surface them to CloudWatch.
49
56
  onError: ({ error }) => {
50
57
  // todo yeild this out
@@ -28,17 +28,17 @@ const mapAiStreamToolInputStart_1 = require("./mapAiStreamToolInputStart");
28
28
  const mapAiStreamToolOutputDenied_1 = require("./mapAiStreamToolOutputDenied");
29
29
  const mapAiStreamToolResult_1 = require("./mapAiStreamToolResult");
30
30
  exports.aiStreamPartMappers = {
31
- 'start': mapAiStreamStart_1.mapAiStreamStart,
32
- 'finish': mapAiStreamFinish_1.mapAiStreamFinish,
31
+ start: mapAiStreamStart_1.mapAiStreamStart,
32
+ finish: mapAiStreamFinish_1.mapAiStreamFinish,
33
33
  'start-step': mapAiStreamStartStep_1.mapAiStreamStartStep,
34
34
  'finish-step': mapAiStreamFinishStep_1.mapAiStreamFinishStep,
35
- 'abort': mapAiStreamAbort_1.mapAiStreamAbort,
36
- 'error': mapAiStreamError_1.mapAiStreamError,
37
- 'raw': mapAiStreamRaw_1.mapAiStreamRaw,
38
- 'source': mapAiStreamSourcePart_1.mapAiStreamSourcePart,
39
- 'file': mapAiStreamFilePart_1.mapAiStreamFilePart,
35
+ abort: mapAiStreamAbort_1.mapAiStreamAbort,
36
+ error: mapAiStreamError_1.mapAiStreamError,
37
+ raw: mapAiStreamRaw_1.mapAiStreamRaw,
38
+ source: mapAiStreamSourcePart_1.mapAiStreamSourcePart,
39
+ file: mapAiStreamFilePart_1.mapAiStreamFilePart,
40
40
  'reasoning-file': mapAiStreamReasoningFilePart_1.mapAiStreamReasoningFilePart,
41
- 'custom': mapAiStreamCustom_1.mapAiStreamCustom,
41
+ custom: mapAiStreamCustom_1.mapAiStreamCustom,
42
42
  'text-start': mapAiStreamTextStart_1.mapAiStreamTextStart,
43
43
  'text-end': mapAiStreamTextEnd_1.mapAiStreamTextEnd,
44
44
  'text-delta': mapAiStreamTextDelta_1.mapAiStreamTextDelta,
@@ -0,0 +1,3 @@
1
+ import { ActionProcessorList, DynamicModuleLoader, QPQConfig, QpqLogger, StorySession, StreamRegistry } from 'quidproquo-core';
2
+ import { AiDriveFileResolver } from './toSdkMessages';
3
+ export declare const createDriveFileResolver: (qpqConfig: QPQConfig, session: StorySession, actionProcessorList: ActionProcessorList, logger: QpqLogger, dynamicModuleLoader: DynamicModuleLoader, streamRegistry?: StreamRegistry) => AiDriveFileResolver;
@@ -0,0 +1,27 @@
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.createDriveFileResolver = void 0;
13
+ const quidproquo_core_1 = require("quidproquo-core");
14
+ const awsLambdaUtils_1 = require("../../../../awsLambdaUtils");
15
+ // Reads drive-referenced files through the active file action processor, so the
16
+ // same message works against S3 in production and local disk under the dev server.
17
+ const createDriveFileResolver = (qpqConfig, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry) => {
18
+ return (drive, filepath) => __awaiter(void 0, void 0, void 0, function* () {
19
+ const resolveStory = (0, quidproquo_core_1.createImplementationRuntime)(qpqConfig, [`AI File: ${drive}/${filepath}`], () => new Date().toISOString(), awsLambdaUtils_1.randomGuid, session, actionProcessorList, logger, dynamicModuleLoader, streamRegistry);
20
+ const storyResult = yield resolveStory(quidproquo_core_1.askFileReadBinaryContents, [drive, filepath]);
21
+ if (storyResult.error) {
22
+ throw new Error(storyResult.error.errorText);
23
+ }
24
+ return storyResult.result;
25
+ });
26
+ };
27
+ exports.createDriveFileResolver = createDriveFileResolver;
@@ -1,4 +1,5 @@
1
1
  export * from './aiStreamPartMappers';
2
+ export * from './createDriveFileResolver';
2
3
  export * from './mapAiStreamAbort';
3
4
  export * from './mapAiStreamCustom';
4
5
  export * from './mapAiStreamError';
@@ -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("./aiStreamPartMappers"), exports);
18
+ __exportStar(require("./createDriveFileResolver"), exports);
18
19
  __exportStar(require("./mapAiStreamAbort"), exports);
19
20
  __exportStar(require("./mapAiStreamCustom"), exports);
20
21
  __exportStar(require("./mapAiStreamError"), exports);
@@ -1,3 +1,4 @@
1
- import { AiMessage } from 'quidproquo-core';
1
+ import { AiMessage, QPQBinaryData } from 'quidproquo-core';
2
2
  import type { ModelMessage } from 'ai';
3
- export declare const toSdkMessages: (messages: AiMessage[]) => ModelMessage[];
3
+ export type AiDriveFileResolver = (drive: string, filepath: string) => Promise<QPQBinaryData>;
4
+ export declare const toSdkMessages: (messages: AiMessage[], resolveDriveFile: AiDriveFileResolver) => Promise<ModelMessage[]>;
@@ -1,28 +1,45 @@
1
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
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.toSdkMessages = void 0;
4
- const mapUserPart = (part) => {
5
- if (part.type === 'text') {
6
- return { type: 'text', text: part.text };
13
+ const mapFilePart = (part, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
14
+ var _a;
15
+ if ('url' in part) {
16
+ return {
17
+ type: 'file',
18
+ data: new URL(part.url),
19
+ mediaType: part.mediaType,
20
+ filename: part.filename,
21
+ };
7
22
  }
23
+ const binary = yield resolveDriveFile(part.drive, part.filepath);
8
24
  return {
9
25
  type: 'file',
10
- data: new URL(part.url),
26
+ data: binary.base64Data,
11
27
  mediaType: part.mediaType,
12
- filename: part.filename,
28
+ filename: (_a = part.filename) !== null && _a !== void 0 ? _a : binary.filename,
13
29
  };
14
- };
15
- const mapAssistantPart = (part) => {
30
+ });
31
+ const mapUserPart = (part, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
32
+ if (part.type === 'text') {
33
+ return { type: 'text', text: part.text };
34
+ }
35
+ return mapFilePart(part, resolveDriveFile);
36
+ });
37
+ const mapAssistantPart = (part, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
16
38
  switch (part.type) {
17
39
  case 'text':
18
40
  return { type: 'text', text: part.text };
19
41
  case 'file':
20
- return {
21
- type: 'file',
22
- data: new URL(part.url),
23
- mediaType: part.mediaType,
24
- filename: part.filename,
25
- };
42
+ return mapFilePart(part, resolveDriveFile);
26
43
  case 'tool-call':
27
44
  return {
28
45
  type: 'tool-call',
@@ -37,7 +54,7 @@ const mapAssistantPart = (part) => {
37
54
  providerOptions: part.providerOptions,
38
55
  };
39
56
  }
40
- };
57
+ });
41
58
  const mapToolResult = (part) => {
42
59
  const output = part.isError
43
60
  ? typeof part.output === 'string'
@@ -53,30 +70,30 @@ const mapToolResult = (part) => {
53
70
  output,
54
71
  };
55
72
  };
56
- const mapUserMessage = (message) => {
73
+ const mapUserMessage = (message, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
57
74
  if (typeof message.content === 'string') {
58
75
  return { role: 'user', content: message.content };
59
76
  }
60
- return { role: 'user', content: message.content.map(mapUserPart) };
61
- };
62
- const mapAssistantMessage = (message) => {
77
+ return { role: 'user', content: yield Promise.all(message.content.map((part) => mapUserPart(part, resolveDriveFile))) };
78
+ });
79
+ const mapAssistantMessage = (message, resolveDriveFile) => __awaiter(void 0, void 0, void 0, function* () {
63
80
  if (typeof message.content === 'string') {
64
81
  return { role: 'assistant', content: message.content };
65
82
  }
66
- return { role: 'assistant', content: message.content.map(mapAssistantPart) };
67
- };
83
+ return { role: 'assistant', content: yield Promise.all(message.content.map((part) => mapAssistantPart(part, resolveDriveFile))) };
84
+ });
68
85
  const mapToolMessage = (message) => ({
69
86
  role: 'tool',
70
87
  content: message.content.map(mapToolResult),
71
88
  });
72
- const toSdkMessages = (messages) => messages.map((message) => {
89
+ const toSdkMessages = (messages, resolveDriveFile) => Promise.all(messages.map((message) => {
73
90
  switch (message.role) {
74
91
  case 'user':
75
- return mapUserMessage(message);
92
+ return mapUserMessage(message, resolveDriveFile);
76
93
  case 'assistant':
77
- return mapAssistantMessage(message);
94
+ return mapAssistantMessage(message, resolveDriveFile);
78
95
  case 'tool':
79
96
  return mapToolMessage(message);
80
97
  }
81
- });
98
+ }));
82
99
  exports.toSdkMessages = toSdkMessages;
@@ -15,7 +15,8 @@ const quidproquo_core_1 = require("quidproquo-core");
15
15
  const quidproquo_webserver_1 = require("quidproquo-webserver");
16
16
  const crypto_1 = require("crypto");
17
17
  const awsNamingUtils_1 = require("../../../awsNamingUtils");
18
- const getApiKeys_1 = require("../../../logic/apiGateway/getApiKeys");
18
+ const getApiKeyValue_1 = require("../../../logic/apiGateway/getApiKeyValue");
19
+ const getExportedValue_1 = require("../../../logic/cloudformation/getExportedValue");
19
20
  // Constant-time string equality. `timingSafeEqual` throws on length mismatch,
20
21
  // so guard the length check — the length itself isn't secret for API keys.
21
22
  const safeEqual = (a, b) => {
@@ -28,17 +29,20 @@ const safeEqual = (a, b) => {
28
29
  };
29
30
  const getProcessApiKeyValidationValidate = (qpqConfig) => {
30
31
  const region = quidproquo_config_aws_1.qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
31
- const application = quidproquo_core_1.qpqCoreUtils.getApplicationName(qpqConfig);
32
- const service = quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
33
- const environment = quidproquo_core_1.qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);
34
- const feature = quidproquo_core_1.qpqCoreUtils.getApplicationModuleFeature(qpqConfig);
35
32
  return (_a) => __awaiter(void 0, [_a], void 0, function* ({ apiKeyValue, apiKeyReferences }) {
36
- const realApiKeys = yield (0, getApiKeys_1.getApiKeys)(region, ...apiKeyReferences.map((apiKey) => {
37
- const apiKeyApplication = apiKey.applicationName || application;
38
- const apiKeyService = apiKey.serviceName || service;
39
- return (0, awsNamingUtils_1.getConfigRuntimeResourceName)(apiKey.name, apiKeyApplication, apiKeyService, environment, feature);
40
- }));
41
- const matched = realApiKeys.some((apiKey) => safeEqual(apiKey.value, apiKeyValue));
33
+ // Fetch each referenced key individually (id via CFN export, then GetApiKey) rather than
34
+ // listing every key in the account - keeps the IAM grant per-key instead of /apikeys-wide.
35
+ const realApiKeyValues = yield Promise.all(apiKeyReferences.map((apiKeyReference) => __awaiter(void 0, void 0, void 0, function* () {
36
+ try {
37
+ const apiKeyId = yield (0, getExportedValue_1.getExportedValue)((0, awsNamingUtils_1.getCFExportNameApiKeyIdFromConfig)(apiKeyReference.name, qpqConfig, apiKeyReference.serviceName, apiKeyReference.applicationName), region);
38
+ return yield (0, getApiKeyValue_1.getApiKeyValue)(region, apiKeyId);
39
+ }
40
+ catch (_a) {
41
+ // An unresolvable reference behaves like the old list-and-filter miss - no match
42
+ return undefined;
43
+ }
44
+ })));
45
+ const matched = realApiKeyValues.some((realApiKeyValue) => safeEqual(realApiKeyValue, apiKeyValue));
42
46
  return (0, quidproquo_core_1.actionResult)(matched);
43
47
  });
44
48
  };
@@ -15,6 +15,7 @@ const quidproquo_webserver_1 = require("quidproquo-webserver");
15
15
  const parseMultipartFormData_1 = require("../../utils/parseMultipartFormData");
16
16
  const getProcessGetRecords = (qpqConfig) => {
17
17
  const serviceName = quidproquo_core_1.qpqCoreUtils.getApplicationModuleName(qpqConfig);
18
+ const fileUploadSettings = quidproquo_webserver_1.qpqWebServerUtils.getFileUploadSettings(qpqConfig);
18
19
  return (_a) => __awaiter(void 0, [_a], void 0, function* ({ eventParams: [apiGatewayEvent, context] }) {
19
20
  // Initialize `path` by removing the service name prefix from `apiGatewayEvent.path`.
20
21
  // This adjustment is necessary because the API gateway routes requests to services based on
@@ -35,7 +36,17 @@ const getProcessGetRecords = (qpqConfig) => {
35
36
  };
36
37
  // Transform the body if its a multipart/form-data
37
38
  if ((quidproquo_webserver_1.qpqWebServerUtils.getHeaderValue('Content-Type', apiGatewayEvent.headers) || '').startsWith('multipart/form-data') && apiGatewayEvent.body) {
38
- internalEventRecord.files = yield (0, parseMultipartFormData_1.parseMultipartFormData)(apiGatewayEvent);
39
+ try {
40
+ internalEventRecord.files = yield (0, parseMultipartFormData_1.parseMultipartFormData)(apiGatewayEvent, fileUploadSettings);
41
+ }
42
+ catch (error) {
43
+ // Stamp the failure on the record so the auto-respond step returns the matching
44
+ // 4xx before the route story runs - throwing here would fail the whole event as a 5xx
45
+ internalEventRecord.fileUploadError =
46
+ error instanceof parseMultipartFormData_1.FileUploadValidationError
47
+ ? { errorType: error.errorType, message: error.message }
48
+ : { errorType: quidproquo_webserver_1.FileUploadErrorTypeEnum.malformed, message: 'Unable to parse multipart/form-data body' };
49
+ }
39
50
  }
40
51
  return (0, quidproquo_core_1.actionResult)([internalEventRecord]);
41
52
  });
@@ -1,3 +1,9 @@
1
1
  import { QPQBinaryData } from 'quidproquo-core';
2
+ import { FileUploadErrorTypeEnum, FileUploadSettings } from 'quidproquo-webserver';
2
3
  import { APIGatewayEvent } from 'aws-lambda';
3
- export declare const parseMultipartFormData: (event: APIGatewayEvent) => Promise<QPQBinaryData[]>;
4
+ export declare class FileUploadValidationError extends Error {
5
+ readonly errorType: FileUploadErrorTypeEnum;
6
+ constructor(errorType: FileUploadErrorTypeEnum, message: string);
7
+ }
8
+ export declare const sanitizeFilename: (filename?: string) => string;
9
+ export declare const parseMultipartFormData: (event: APIGatewayEvent, fileUploadSettings: FileUploadSettings) => Promise<QPQBinaryData[]>;
@@ -3,42 +3,111 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.parseMultipartFormData = void 0;
6
+ exports.parseMultipartFormData = exports.sanitizeFilename = exports.FileUploadValidationError = void 0;
7
+ const quidproquo_webserver_1 = require("quidproquo-webserver");
7
8
  const busboy_1 = __importDefault(require("busboy"));
8
- const parseMultipartFormData = (event) => new Promise((resolve, reject) => {
9
+ class FileUploadValidationError extends Error {
10
+ constructor(errorType, message) {
11
+ super(message);
12
+ this.errorType = errorType;
13
+ this.name = 'FileUploadValidationError';
14
+ }
15
+ }
16
+ exports.FileUploadValidationError = FileUploadValidationError;
17
+ // Reduce a client-supplied filename to a bare, path-traversal-safe name.
18
+ const sanitizeFilename = (filename) => {
19
+ const baseName = (filename || '')
20
+ .split(/[/\\]/)
21
+ .filter((segment) => segment.length > 0)
22
+ .pop();
23
+ // eslint-disable-next-line no-control-regex
24
+ const cleaned = (baseName || '').replace(/[\x00-\x1f]/g, '');
25
+ if (!cleaned || cleaned === '.' || cleaned === '..') {
26
+ return 'unnamed';
27
+ }
28
+ return cleaned;
29
+ };
30
+ exports.sanitizeFilename = sanitizeFilename;
31
+ const isMimeTypeAllowed = (mimeType, allowedMimeTypes) => {
32
+ if (!allowedMimeTypes) {
33
+ return true;
34
+ }
35
+ const normalizedMimeType = (mimeType || '').toLowerCase();
36
+ return allowedMimeTypes.some((allowed) => {
37
+ const normalizedAllowed = allowed.toLowerCase();
38
+ return normalizedAllowed.endsWith('/*')
39
+ ? normalizedMimeType.startsWith(normalizedAllowed.slice(0, -1))
40
+ : normalizedMimeType === normalizedAllowed;
41
+ });
42
+ };
43
+ const parseMultipartFormData = (event, fileUploadSettings) => new Promise((resolve, reject) => {
9
44
  const busboy = (0, busboy_1.default)({
10
45
  headers: {
11
46
  'content-type': event.headers['content-type'] || event.headers['Content-Type'],
12
47
  },
48
+ limits: {
49
+ fileSize: fileUploadSettings.maxFileSizeBytes,
50
+ files: fileUploadSettings.maxFileCount,
51
+ fields: fileUploadSettings.maxFieldCount,
52
+ fieldSize: fileUploadSettings.maxFieldSizeBytes,
53
+ },
13
54
  });
14
55
  const result = {
15
56
  files: [],
16
57
  fields: {},
17
58
  };
59
+ let settled = false;
60
+ const rejectOnce = (error) => {
61
+ if (!settled) {
62
+ settled = true;
63
+ reject(error);
64
+ }
65
+ };
18
66
  busboy.on('file', (name, file, info) => {
19
- let fileBuffer = Buffer.alloc(0);
67
+ const filename = (0, exports.sanitizeFilename)(info.filename);
68
+ if (!isMimeTypeAllowed(info.mimeType, fileUploadSettings.allowedMimeTypes)) {
69
+ rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.disallowedMimeType, `File "${filename}" has disallowed content type "${info.mimeType}"`));
70
+ file.resume();
71
+ return;
72
+ }
73
+ const chunks = [];
20
74
  file.on('data', (data) => {
21
- fileBuffer = Buffer.concat([fileBuffer, data]); // Concatenate the new data to the existing buffer
75
+ chunks.push(data);
76
+ });
77
+ // Emitted when the stream is truncated at limits.fileSize - a silently truncated upload must fail, not succeed
78
+ file.on('limit', () => {
79
+ rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.fileTooLarge, `File "${filename}" exceeds the maximum file size of ${fileUploadSettings.maxFileSizeBytes} bytes`));
22
80
  });
23
81
  file.on('end', () => {
24
- if (fileBuffer.length > 0) {
82
+ const content = Buffer.concat(chunks);
83
+ if (!settled && content.length > 0) {
25
84
  result.files.push({
26
- filename: info.filename,
85
+ filename,
27
86
  contentType: info.mimeType,
28
87
  encoding: info.encoding,
29
88
  name: name,
30
- content: fileBuffer,
89
+ content,
31
90
  });
32
91
  }
33
92
  });
34
93
  });
94
+ busboy.on('filesLimit', () => {
95
+ rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.tooManyFiles, `Upload exceeds the maximum of ${fileUploadSettings.maxFileCount} files`));
96
+ });
97
+ busboy.on('fieldsLimit', () => {
98
+ rejectOnce(new FileUploadValidationError(quidproquo_webserver_1.FileUploadErrorTypeEnum.tooManyFields, `Upload exceeds the maximum of ${fileUploadSettings.maxFieldCount} fields`));
99
+ });
35
100
  busboy.on('field', (name, value) => {
36
101
  result.fields[name] = value;
37
102
  });
38
103
  busboy.on('error', (error) => {
39
- reject(error);
104
+ rejectOnce(error);
40
105
  });
41
106
  busboy.on('finish', () => {
107
+ if (settled) {
108
+ return;
109
+ }
110
+ settled = true;
42
111
  const qpqBinaryFiles = result.files.map((f) => ({
43
112
  filename: f.filename,
44
113
  mimetype: f.contentType,
@@ -15,7 +15,7 @@ const quidproquo_core_1 = require("quidproquo-core");
15
15
  const awsNamingUtils_1 = require("../../../awsNamingUtils");
16
16
  const publishMessage_1 = require("../../../logic/sns/publishMessage");
17
17
  const getProcessEventBusSendMessage = (qpqConfig) => {
18
- return (_a, session_1) => __awaiter(void 0, [_a, session_1], void 0, function* ({ eventBusName, eventBusMessages, }, session) {
18
+ return (_a, session_1) => __awaiter(void 0, [_a, session_1], void 0, function* ({ eventBusName, eventBusMessages }, session) {
19
19
  var _b, _c, _d, _e, _f;
20
20
  const region = quidproquo_config_aws_1.qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);
21
21
  const eventBusConfig = quidproquo_core_1.qpqCoreUtils.getEventBusConfigByName(eventBusName, qpqConfig);
@@ -81,9 +81,7 @@ const getProcessFileStreamOpen = (qpqConfig) => {
81
81
  const streamId = `s3-${(0, awsLambdaUtils_1.randomGuid)()}`;
82
82
  const isText = encoding === 'text';
83
83
  const size = chunkSize !== null && chunkSize !== void 0 ? chunkSize : 65536;
84
- const transform = isText
85
- ? (buf) => buf.toString('utf8')
86
- : (buf) => buf.toString('base64');
84
+ const transform = isText ? (buf) => buf.toString('utf8') : (buf) => buf.toString('base64');
87
85
  const iterator = chunkedReadableIterator(response.Body, size, transform);
88
86
  streamRegistry.register(streamId, iterator);
89
87
  return (0, quidproquo_core_1.actionResult)({ id: streamId, encoding });
@@ -6,6 +6,7 @@ export * from './eventBus';
6
6
  export * from './file';
7
7
  export * from './graphDatabase';
8
8
  export * from './keyValueStore';
9
+ export * from './metric';
9
10
  export * from './queue';
10
11
  export * from './system';
11
12
  export * from './userDirectory';
@@ -32,6 +32,7 @@ __exportStar(require("./eventBus"), exports);
32
32
  __exportStar(require("./file"), exports);
33
33
  __exportStar(require("./graphDatabase"), exports);
34
34
  __exportStar(require("./keyValueStore"), exports);
35
+ __exportStar(require("./metric"), exports);
35
36
  __exportStar(require("./queue"), exports);
36
37
  __exportStar(require("./system"), exports);
37
38
  __exportStar(require("./userDirectory"), exports);
@@ -44,10 +45,11 @@ const eventBus_1 = require("./eventBus");
44
45
  const file_1 = require("./file");
45
46
  const graphDatabase_1 = require("./graphDatabase");
46
47
  const keyValueStore_1 = require("./keyValueStore");
48
+ const metric_1 = require("./metric");
47
49
  const queue_1 = require("./queue");
48
50
  const system_1 = require("./system");
49
51
  const userDirectory_1 = require("./userDirectory");
50
52
  const getCoreActionProcessor = (qpqConfig, dynamicModuleLoader) => __awaiter(void 0, void 0, void 0, function* () {
51
- return (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (yield (0, ai_1.getAiActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, apiKeyValidation_1.getApiKeyValidationActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, file_1.getFileActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, keyValueStore_1.getKeyValueStoreActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, queue_1.getQueueActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, userDirectory_1.getUserDirectoryActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, eventBus_1.getEventBusActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, system_1.getSystemActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, config_1.getConfigActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, event_1.getApiGatewayApiEventEventProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, graphDatabase_1.getGraphDatabaseActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, quidproquo_webserver_1.getRouteAuthValidationActionProcessor)(qpqConfig, dynamicModuleLoader))));
53
+ return (Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (yield (0, ai_1.getAiActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, apiKeyValidation_1.getApiKeyValidationActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, file_1.getFileActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, keyValueStore_1.getKeyValueStoreActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, metric_1.getMetricActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, queue_1.getQueueActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, userDirectory_1.getUserDirectoryActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, eventBus_1.getEventBusActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, system_1.getSystemActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, config_1.getConfigActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, event_1.getApiGatewayApiEventEventProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, graphDatabase_1.getGraphDatabaseActionProcessor)(qpqConfig, dynamicModuleLoader))), (yield (0, quidproquo_webserver_1.getRouteAuthValidationActionProcessor)(qpqConfig, dynamicModuleLoader))));
52
54
  });
53
55
  exports.getCoreActionProcessor = getCoreActionProcessor;
@@ -32,6 +32,7 @@ const getProcessKeyValueStoreUpsert = (qpqConfig) => {
32
32
  try {
33
33
  yield (0, dynamo_1.putItem)(dynamoTableName, item, keys, {
34
34
  expires: options === null || options === void 0 ? void 0 : options.ttlInSeconds,
35
+ ifNotExistsAttribute: (options === null || options === void 0 ? void 0 : options.ifNotExists) ? storeConfig.partitionKey.key : undefined,
35
36
  }, region);
36
37
  return (0, quidproquo_core_1.actionResult)(void 0);
37
38
  }
@@ -39,6 +40,7 @@ const getProcessKeyValueStoreUpsert = (qpqConfig) => {
39
40
  return (0, quidproquo_core_1.actionResultErrorFromCaughtError)(error, {
40
41
  InternalServerError: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_2.KeyValueStoreUpsertErrorTypeEnum.ServiceUnavailable, 'KVS Service Unavailable'),
41
42
  ResourceNotFoundException: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_2.KeyValueStoreUpsertErrorTypeEnum.ResourceNotFound, 'KVS Resource Not Found'),
43
+ ConditionalCheckFailedException: () => (0, quidproquo_core_1.actionResultError)(quidproquo_core_2.KeyValueStoreUpsertErrorTypeEnum.Conflict, 'KVS item already exists'),
42
44
  });
43
45
  }
44
46
  });
@@ -0,0 +1,2 @@
1
+ import { ActionProcessorListResolver } from 'quidproquo-core';
2
+ export declare const getMetricPutActionProcessor: ActionProcessorListResolver;