@reventlessdev/reventless-aws 3.0.0-alpha.220 → 3.0.0-alpha.221

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 (160) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +8 -8
  3. package/src/Platform.res.mjs +4 -0
  4. package/src/Platform_Stack.res +6 -0
  5. package/src/Platform_Stack.res.mjs +3 -1
  6. package/src/adapter/AWS_Tags.res +127 -12
  7. package/src/adapter/AWS_Tags.res.mjs +58 -17
  8. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +13 -1
  9. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +3 -4
  10. package/src/adapter/Api/Platform_UIFragments_Lambda.res +13 -1
  11. package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +3 -4
  12. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +12 -0
  13. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res.mjs +4 -2
  14. package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +6 -0
  15. package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res.mjs +2 -1
  16. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +2 -2
  17. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res.mjs +2 -2
  18. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +2 -2
  19. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs +2 -2
  20. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +2 -2
  21. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res.mjs +2 -2
  22. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +2 -2
  23. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs +2 -2
  24. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +1 -0
  25. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +1 -1
  26. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +1 -1
  27. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res.mjs +1 -2
  28. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +1 -1
  29. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +1 -1
  30. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +2 -2
  31. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res.mjs +2 -2
  32. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +2 -2
  33. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res.mjs +2 -2
  34. package/src/adapter/EventLog/EventLogStorage.res +3 -3
  35. package/src/adapter/EventLog/EventLogStorage.res.mjs +3 -3
  36. package/src/adapter/EventLog/EventLogStorage_DynamoDb.res +2 -2
  37. package/src/adapter/EventLog/EventLogStorage_DynamoDb.res.mjs +2 -2
  38. package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +2 -2
  39. package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res.mjs +2 -2
  40. package/src/adapter/EventLog/EventLogStorage_Postgres.res +1 -1
  41. package/src/adapter/EventLog/EventLogStorage_Postgres.res.mjs +1 -1
  42. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +7 -1
  43. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +2 -2
  44. package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +1 -1
  45. package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res.mjs +1 -1
  46. package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +2 -2
  47. package/src/adapter/EventTopic/EventTopicPublisher_SNS.res.mjs +2 -2
  48. package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +2 -2
  49. package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res.mjs +2 -2
  50. package/src/adapter/Mcp/MCP_Lambda.res.mjs +1 -1
  51. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +7 -0
  52. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +4 -2
  53. package/src/adapter/Postgres/PgMigration_Builder.res +1 -0
  54. package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
  55. package/src/adapter/QueryDb/PgQueryResolver_Builder.res +7 -0
  56. package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +4 -2
  57. package/src/adapter/QueryDb/QueryDbStorage.res +6 -6
  58. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +6 -6
  59. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +2 -2
  60. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs +2 -2
  61. package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +2 -2
  62. package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res.mjs +2 -2
  63. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +1 -1
  64. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +1 -1
  65. package/src/adapter/Runtime/AdminEventCollectorEntryPoint.mjs +24 -41
  66. package/src/adapter/Runtime/AggregateEntryPoint.mjs +52 -45
  67. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +19 -2
  68. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +8 -5
  69. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +19 -2
  70. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res.mjs +8 -5
  71. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +9 -1
  72. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res.mjs +4 -2
  73. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +9 -1
  74. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res.mjs +4 -2
  75. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +9 -1
  76. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +4 -2
  77. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +9 -1
  78. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +4 -2
  79. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +36 -22
  80. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +1 -0
  81. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +1 -1
  82. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +39 -42
  83. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +7 -1
  84. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +4 -2
  85. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +19 -1
  86. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +5 -2
  87. package/src/adapter/Runtime/EventMapperEntryPoint.mjs +25 -23
  88. package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +25 -41
  89. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +1 -0
  90. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +1 -1
  91. package/src/adapter/Runtime/HandlerFactoryHelpers.mjs +126 -0
  92. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs +24 -41
  93. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +33 -24
  94. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +52 -2
  95. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +25 -7
  96. package/src/adapter/Runtime/SideEffectEntryPoint.mjs +25 -24
  97. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +9 -1
  98. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +4 -2
  99. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +1 -0
  100. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +1 -1
  101. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -21
  102. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +2 -1
  103. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +2 -2
  104. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +1 -0
  105. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res.mjs +1 -1
  106. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +12 -0
  107. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res.mjs +4 -2
  108. package/src/adapter/StateTopic/StateTopic_AppSync.res +7 -1
  109. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +2 -2
  110. package/src/adapter/Task/TaskBucket_S3.res +1 -0
  111. package/src/adapter/Task/TaskBucket_S3.res.mjs +7 -1
  112. package/src/components/Aggregate_Builder_Micro.res.mjs +2 -0
  113. package/src/components/Aggregate_Builder_NoResolver.res.mjs +2 -0
  114. package/src/components/Aggregate_Builder_PerAggregate.res.mjs +2 -0
  115. package/src/components/Aggregate_Builder_Single.res.mjs +2 -0
  116. package/src/components/Aggregate_Builder_Single_Async.res.mjs +2 -0
  117. package/src/components/Api/AppSync_Adapter.res +12 -0
  118. package/src/components/Api/AppSync_Adapter.res.mjs +6 -2
  119. package/src/components/Api/AppSync_MergedApi.res +12 -0
  120. package/src/components/Api/AppSync_MergedApi.res.mjs +5 -2
  121. package/src/components/AutomationSlice_Builder.res.mjs +2 -0
  122. package/src/components/ExtensionPoint_Builder.res.mjs +2 -0
  123. package/src/components/ForeignReadModel_Builder.res.mjs +4 -0
  124. package/src/components/OutboundTranslationSlice_Builder.res.mjs +2 -0
  125. package/src/components/Plugin.res.mjs +2 -0
  126. package/src/components/ReadModel_Builder_NoResolver.res.mjs +2 -0
  127. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +2 -0
  128. package/src/components/ReadModel_Builder_PerReadModel.res.mjs +2 -0
  129. package/src/components/ReadModel_Builder_Single.res.mjs +2 -0
  130. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +2 -0
  131. package/src/components/SideEffectHandler_PerSideEffectHandler.res.mjs +6 -0
  132. package/src/components/SideEffectHandler_Single.res.mjs +4 -0
  133. package/src/components/StateViewSlice_Builder.res.mjs +2 -0
  134. package/src/components/StateViewSlice_Builder_Stream.res.mjs +2 -0
  135. package/src/components/Task_Builder_PerBucket.res.mjs +4 -0
  136. package/src/components/Vpc.res +10 -1
  137. package/src/components/Vpc.res.mjs +3 -1
  138. package/src/plugin/cloner/ClonerRunner_Fargate.res +26 -1
  139. package/src/plugin/cloner/ClonerRunner_Fargate.res.mjs +9 -6
  140. package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +6 -0
  141. package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res.mjs +4 -1
  142. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +1 -0
  143. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -1
  144. package/src/plugin/runtime/PluginRuntime_Builder.res +2 -0
  145. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +2 -2
  146. package/src/plugin/stack/Plugin_ExtensionPoint_Builder.res.mjs +4 -0
  147. package/src/plugin/stack/Plugin_Stack.res +26 -1
  148. package/src/plugin/stack/Plugin_Stack.res.mjs +8 -3
  149. package/src/util/Util_CognitoGroupUser.res +6 -0
  150. package/src/util/Util_CognitoGroupUser.res.mjs +3 -1
  151. package/src/util/Util_DeadLetterQueue.res +8 -2
  152. package/src/util/Util_DeadLetterQueue.res.mjs +3 -4
  153. package/src/util/Util_LogAttribution.res +31 -0
  154. package/src/util/Util_LogAttribution.res.mjs +27 -0
  155. package/tests/AWS_TagsTest.res +236 -0
  156. package/tests/AWS_TagsTest.res.mjs +153 -0
  157. package/tests/HandlerFactoryDispatchTest.res +190 -0
  158. package/tests/HandlerFactoryDispatchTest.res.mjs +135 -0
  159. package/tests/integration/AggregateEntryPoint_IntegrationTest.res +5 -3
  160. package/tests/integration/AggregateEntryPoint_IntegrationTest.res.mjs +5 -3
@@ -8,6 +8,7 @@ import * as Aggregate$ReventlessCore from "@reventlessdev/reventless-core/src/co
8
8
  import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
9
9
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
10
10
  import * as ComponentType$ReventlessCore from "@reventlessdev/reventless-core/src/ComponentType.res.mjs";
11
+ import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
11
12
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
12
13
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
13
14
 
@@ -138,11 +139,12 @@ function finish() {
138
139
  let name = ComponentType$ReventlessCore.name(spec.aggregateName, Aggregate$ReventlessCore.componentType);
139
140
  let specModule = Stdlib_Option.getOr(JSON.stringify(info.specModulePath), `""`);
140
141
  let behaviorModule = Stdlib_Option.getOr(JSON.stringify(info.behaviorModulePath), `""`);
142
+ let pluginFragment = Util_LogAttribution$ReventlessAws.pluginFragment(`AggregateRuntime(` + spec.aggregateName + `)`);
141
143
  let handlerConfigOutput = Pulumi.all([
142
144
  info.eventLogTableName,
143
145
  spec.queueUrl,
144
146
  spec.queueArn
145
- ]).apply(param => `{"handlers":[{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"}]}`);
147
+ ]).apply(param => `{"handlers":[{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment + `}]}`);
146
148
  let envVars = {};
147
149
  envVars["HANDLER_CONFIG"] = handlerConfigOutput;
148
150
  envVars["DISPATCH_MODE"] = "async";
@@ -152,7 +154,7 @@ function finish() {
152
154
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(specPkg);
153
155
  packageDirs[behaviorPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(behaviorPkg);
154
156
  let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
155
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "CommandHandler", match.code, match.sourceCodeHash, envVars, spec.memorySize, spec.timeout, undefined, undefined, undefined, undefined, undefined, aggregateOpts);
157
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "CommandHandler", "Aggregate", match.code, match.sourceCodeHash, envVars, spec.memorySize, spec.timeout, undefined, undefined, undefined, undefined, undefined, aggregateOpts);
156
158
  spec.connects.forEach(connect => connect(runtime));
157
159
  let channelSpecs = Stdlib_Option.mapOr(spec.eventCollectorChannelSpec, [], cs => [cs]);
158
160
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect(name, channelSpecs, runtime, aggregateOpts);
@@ -250,6 +250,13 @@ let finish = () =>
250
250
  let behaviorModule =
251
251
  info.behaviorModulePath->JSON.stringifyAny->Option.getOr(`""`)
252
252
 
253
+ // `plugin` cannot be resolved inside the Lambda — LogPrefix's registry is a
254
+ // deploy-time structure, and the Lambda-name fallback yields nothing for a
255
+ // shared or per-aggregate command handler. Resolve it here; the shell still
256
+ // derives `comp` itself from the spec name.
257
+ let pluginFragment = Util_LogAttribution.pluginFragment(
258
+ ~comp=`AggregateRuntime(${spec.aggregateName})`,
259
+ )
253
260
  let handlerJson =
254
261
  Pulumi.Output.all4((
255
262
  info.eventLogTableName,
@@ -258,7 +265,7 @@ let finish = () =>
258
265
  pgConnectionFragment,
259
266
  ))
260
267
  ->Pulumi.Output.apply(((table, queueUrl, queueArn, pgFragment)) =>
261
- `{"specModule":${specModule},"behaviorModule":${behaviorModule},"eventLogTable":"${table}","queueUrl":"${queueUrl}","queueArn":"${queueArn}"${pgFragment}}`
268
+ `{"specModule":${specModule},"behaviorModule":${behaviorModule},"eventLogTable":"${table}","queueUrl":"${queueUrl}","queueArn":"${queueArn}"${pluginFragment}${pgFragment}}`
262
269
  )
263
270
  let _ = handlerOutputs->Array.push(handlerJson)
264
271
  | None =>
@@ -327,6 +334,7 @@ let finish = () =>
327
334
  let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
328
335
  ~name="AllAggregatesCmdHandler",
329
336
  ~unitKind=ReventlessCore.Monitoring.CommandHandler,
337
+ ~componentKind=ReventlessCore.ComponentType.Aggregate,
330
338
  ~code,
331
339
  ~sourceCodeHash,
332
340
  ~envVars,
@@ -13,6 +13,7 @@ import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/sr
13
13
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
14
14
  import * as PgConnection$ReventlessAws from "../Postgres/PgConnection.res.mjs";
15
15
  import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.mjs";
16
+ import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
16
17
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
17
18
  import * as CommandTopicChannel_SQS_Sync$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs";
18
19
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
@@ -174,12 +175,13 @@ function finish() {
174
175
  packageDirs[behaviorPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(behaviorPkg);
175
176
  let specModule = Stdlib_Option.getOr(JSON.stringify(info.specModulePath), `""`);
176
177
  let behaviorModule = Stdlib_Option.getOr(JSON.stringify(info.behaviorModulePath), `""`);
178
+ let pluginFragment = Util_LogAttribution$ReventlessAws.pluginFragment(`AggregateRuntime(` + spec.aggregateName + `)`);
177
179
  let handlerJson = Pulumi.all([
178
180
  info.eventLogTableName,
179
181
  spec.queueUrl,
180
182
  spec.queueArn,
181
183
  pgConnectionFragment
182
- ]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + param[3] + `}`);
184
+ ]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment + param[3] + `}`);
183
185
  handlerOutputs.push(handlerJson);
184
186
  });
185
187
  let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
@@ -201,7 +203,7 @@ function finish() {
201
203
  subnetIds: pgSelection.subnetIds,
202
204
  securityGroupIds: [sgId]
203
205
  })) : undefined;
204
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAggregatesCmdHandler", "CommandHandler", match.code, match.sourceCodeHash, envVars, memorySize, timeout, cfg.reservedConcurrency, cfg.ephemeralStorageMb, cfg.logRetentionDays, undefined, vpcConfig, opts);
206
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAggregatesCmdHandler", "CommandHandler", "Aggregate", match.code, match.sourceCodeHash, envVars, memorySize, timeout, cfg.reservedConcurrency, cfg.ephemeralStorageMb, cfg.logRetentionDays, undefined, vpcConfig, opts);
205
207
  if (pgSelection !== undefined) {
206
208
  pgSelection.connectionConfig.apply(cc => new (Aws.iam.RolePolicy)("AllAggregatesCmdHandler-pgSecret", {
207
209
  policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, "AllAggregatesCmdHandler-pgSecretPolicy", [{
@@ -231,6 +231,13 @@ let finish = () =>
231
231
  let behaviorModule =
232
232
  info.behaviorModulePath->JSON.stringifyAny->Option.getOr(`""`)
233
233
 
234
+ // `plugin` cannot be resolved inside the Lambda — LogPrefix's registry is a
235
+ // deploy-time structure, and the Lambda-name fallback yields nothing for a
236
+ // shared or per-aggregate command handler. Resolve it here; the shell still
237
+ // derives `comp` itself from the spec name.
238
+ let pluginFragment = Util_LogAttribution.pluginFragment(
239
+ ~comp=`AggregateRuntime(${spec.aggregateName})`,
240
+ )
234
241
  let handlerJson =
235
242
  Pulumi.Output.all4((
236
243
  info.eventLogTableName,
@@ -239,7 +246,7 @@ let finish = () =>
239
246
  pgConnectionFragment,
240
247
  ))
241
248
  ->Pulumi.Output.apply(((table, queueUrl, queueArn, pgFragment)) =>
242
- `{"specModule":${specModule},"behaviorModule":${behaviorModule},"eventLogTable":"${table}","queueUrl":"${queueUrl}","queueArn":"${queueArn}"${pgFragment}}`
249
+ `{"specModule":${specModule},"behaviorModule":${behaviorModule},"eventLogTable":"${table}","queueUrl":"${queueUrl}","queueArn":"${queueArn}"${pluginFragment}${pgFragment}}`
243
250
  )
244
251
  let _ = handlerOutputs->Array.push(handlerJson)
245
252
  | None =>
@@ -302,6 +309,7 @@ let finish = () =>
302
309
  let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
303
310
  ~name="AllAggregatesAsyncCmdHandler",
304
311
  ~unitKind=ReventlessCore.Monitoring.CommandHandler,
312
+ ~componentKind=ReventlessCore.ComponentType.Aggregate,
305
313
  ~code,
306
314
  ~sourceCodeHash,
307
315
  ~envVars,
@@ -13,6 +13,7 @@ import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/sr
13
13
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
14
14
  import * as PgConnection$ReventlessAws from "../Postgres/PgConnection.res.mjs";
15
15
  import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.mjs";
16
+ import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
16
17
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
17
18
  import * as CommandTopicChannel_SQS_Async$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Async.res.mjs";
18
19
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
@@ -166,12 +167,13 @@ function finish() {
166
167
  packageDirs[behaviorPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(behaviorPkg);
167
168
  let specModule = Stdlib_Option.getOr(JSON.stringify(info.specModulePath), `""`);
168
169
  let behaviorModule = Stdlib_Option.getOr(JSON.stringify(info.behaviorModulePath), `""`);
170
+ let pluginFragment = Util_LogAttribution$ReventlessAws.pluginFragment(`AggregateRuntime(` + spec.aggregateName + `)`);
169
171
  let handlerJson = Pulumi.all([
170
172
  info.eventLogTableName,
171
173
  spec.queueUrl,
172
174
  spec.queueArn,
173
175
  pgConnectionFragment
174
- ]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + param[3] + `}`);
176
+ ]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment + param[3] + `}`);
175
177
  handlerOutputs.push(handlerJson);
176
178
  });
177
179
  let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
@@ -190,7 +192,7 @@ function finish() {
190
192
  subnetIds: pgSelection.subnetIds,
191
193
  securityGroupIds: [sgId]
192
194
  })) : undefined;
193
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAggregatesAsyncCmdHandler", "CommandHandler", match.code, match.sourceCodeHash, envVars, memorySize, timeout, cfg.reservedConcurrency, cfg.ephemeralStorageMb, cfg.logRetentionDays, undefined, vpcConfig, opts);
195
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAggregatesAsyncCmdHandler", "CommandHandler", "Aggregate", match.code, match.sourceCodeHash, envVars, memorySize, timeout, cfg.reservedConcurrency, cfg.ephemeralStorageMb, cfg.logRetentionDays, undefined, vpcConfig, opts);
194
196
  if (pgSelection !== undefined) {
195
197
  pgSelection.connectionConfig.apply(cc => new (Aws.iam.RolePolicy)("AllAggregatesAsyncCmdHandler-pgSecret", {
196
198
  policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, "AllAggregatesAsyncCmdHandler-pgSecretPolicy", [{
@@ -7,8 +7,16 @@ import * as Effect from "effect/Effect";
7
7
  import * as Stream from "effect/Stream";
8
8
  import * as Chunk from "effect/Chunk";
9
9
  import { parseJsonOrThrow } from "sury/src/S.res.mjs";
10
- import { makeTableRef, makeQueueRef, log, pluginName } from "./HandlerFactoryHelpers.mjs";
11
- import { tag as requestContextTag } from "@reventlessdev/reventless-core/src/RequestContext.res.mjs";
10
+ import {
11
+ makeTableRef,
12
+ makeQueueRef,
13
+ log,
14
+ runEffect,
15
+ setRequestId,
16
+ extractMetaField,
17
+ extractSentTimestamp,
18
+ extractRetryCount,
19
+ } from "./HandlerFactoryHelpers.mjs";
12
20
  import { save as qdbSave } from "@reventlessdev/reventless-aws/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res.mjs";
13
21
  import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
14
22
  import { Make as automationSliceCallbackMake } from "@reventlessdev/reventless-core/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs";
@@ -17,21 +25,13 @@ import { Make as outboundTranslationSliceCallbackMake } from "@reventlessdev/rev
17
25
 
18
26
  const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
19
27
 
20
- // Set at handler entry; read by runEffect to tag logs with the Lambda request id.
21
- let _currentRequestId = "unknown";
22
-
23
- function runEffect(correlationId, effect) {
24
- let e = effect
25
- // Promote correlationId/requestId/plugin to top-level JSON log fields —
26
- // decoded by EffectLogger.install() from Effect log annotations. Harmless
27
- // no-op if the unified logger isn't installed.
28
- .pipe(Effect.annotateLogs("correlationId", correlationId || "unknown"))
29
- .pipe(Effect.annotateLogs("requestId", _currentRequestId));
30
- if (pluginName) e = e.pipe(Effect.annotateLogs("plugin", pluginName));
31
- return e
32
- .pipe(Effect.provideService(requestContextTag, { correlationId: correlationId || "unknown" }))
33
- .pipe(Effect.runPromise);
34
- }
28
+ // Same comp the slice's own callback logs under (AutomationSlice_Callback /
29
+ // OutboundTranslationSlice_Callback), so a filter catches both the framework's
30
+ // lines and the application handler's.
31
+ const sliceComp = (callbackType, sliceName) =>
32
+ callbackType === "outbound"
33
+ ? `OutboundTranslationSlice(${sliceName})`
34
+ : `AutomationSlice(${sliceName})`;
35
35
 
36
36
  function groupBySource(records) {
37
37
  const dict = {};
@@ -95,8 +95,12 @@ async function buildAllHandlers() {
95
95
  // Multiple slices can share one source stream (e.g. several automation
96
96
  // slices reacting to the same DcbEventLog). Accumulate all handlers per
97
97
  // source ARN; a plain assignment collapses them to one (Promise.all race),
98
- // silently dropping the rest.
99
- (handlers[h.sourceUrn] ||= []).push(handler);
98
+ // silently dropping the rest. Each keeps its own comp — that is what
99
+ // separates co-hosted slices in the log stream.
100
+ (handlers[h.sourceUrn] ||= []).push({
101
+ handler,
102
+ comp: sliceComp(h.callbackType, specModule.name),
103
+ });
100
104
  }));
101
105
  return handlers;
102
106
  }
@@ -104,7 +108,7 @@ async function buildAllHandlers() {
104
108
  const initPromise = buildAllHandlers();
105
109
 
106
110
  export async function handler(event, context) {
107
- _currentRequestId = context?.awsRequestId || "unknown";
111
+ setRequestId(context?.awsRequestId);
108
112
  const handlers = await initPromise;
109
113
  const records = event.Records || [];
110
114
  const grouped = groupBySource(records);
@@ -113,8 +117,18 @@ export async function handler(event, context) {
113
117
  const streamHandlers = handlers[arn];
114
118
  if (streamHandlers !== undefined && streamHandlers.length > 0) {
115
119
  log.debug("found " + streamHandlers.length + " handler(s) for " + arn, { comp: "AutomationSliceRuntime" });
116
- await Promise.all(streamHandlers.map(h =>
117
- runEffect(undefined, h({ Records: subRecords }, context))
120
+ const correlationId = extractMetaField(subRecords, "correlationId");
121
+ const causationId = extractMetaField(subRecords, "causationId");
122
+ const timestamp = extractSentTimestamp(subRecords);
123
+ const retryCount = extractRetryCount(subRecords);
124
+ await Promise.all(streamHandlers.map(({ handler: h, comp }) =>
125
+ runEffect(h({ Records: subRecords }, context), {
126
+ correlationId,
127
+ causationId,
128
+ comp,
129
+ timestamp,
130
+ retryCount,
131
+ })
118
132
  ));
119
133
  } else {
120
134
  log.warn("no handler found: " + arn, { comp: "AutomationSliceRuntime" });
@@ -166,6 +166,7 @@ let finish = () =>
166
166
  let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
167
167
  ~name="AllAutomationSlices",
168
168
  ~unitKind=ReventlessCore.Monitoring.Reactor,
169
+ ~componentKind=ReventlessCore.ComponentType.AutomationSlice,
169
170
  ~code,
170
171
  ~sourceCodeHash,
171
172
  ~envVars,
@@ -120,7 +120,7 @@ function finish() {
120
120
  let envVars = {};
121
121
  envVars["HANDLER_CONFIG"] = handlerConfigOutput;
122
122
  let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined);
123
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAutomationSlices", "Reactor", match$1.code, match$1.sourceCodeHash, envVars, match[0], match[1], undefined, undefined, undefined, undefined, undefined, opts);
123
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAutomationSlices", "Reactor", "AutomationSlice", match$1.code, match$1.sourceCodeHash, envVars, match[0], match[1], undefined, undefined, undefined, undefined, undefined, opts);
124
124
  let channelSpecs = storedSpecs.map(param => param.channelSpec);
125
125
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllAutomationSlices", channelSpecs, runtime, opts);
126
126
  } else {
@@ -6,8 +6,16 @@
6
6
  import * as Chunk from "effect/Chunk";
7
7
  import * as Effect from "effect/Effect";
8
8
  import * as Stream from "effect/Stream";
9
- import { patchSpecId, makeQueueRef, log, pluginName } from "./HandlerFactoryHelpers.mjs";
10
- import { tag as requestContextTag } from "@reventlessdev/reventless-core/src/RequestContext.res.mjs";
9
+ import {
10
+ patchSpecId,
11
+ makeQueueRef,
12
+ log,
13
+ runEffect,
14
+ setRequestId,
15
+ extractMetaField,
16
+ extractSentTimestamp,
17
+ extractRetryCount,
18
+ } from "./HandlerFactoryHelpers.mjs";
11
19
  import { Make as dcbEventLogOperationsMake } from "@reventlessdev/reventless-core/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs";
12
20
  import { makeCommandGenerator } from "./CommandGeneratorEntryPoint_Ops.res.mjs";
13
21
  import { commandOutcomeToJson, runInlineAndCollect } from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs";
@@ -39,39 +47,12 @@ function extractTypeName(json) {
39
47
  return undefined;
40
48
  }
41
49
 
42
- // Set at handler entry; read by runEffect to tag logs with the Lambda request id.
43
- let _currentRequestId = "unknown";
44
-
45
- function runEffect(correlationId, effect) {
46
- let e = effect
47
- // Promote correlationId/requestId/plugin to top-level JSON log fields —
48
- // decoded by EffectLogger.install() from Effect log annotations. Harmless
49
- // no-op if the unified logger isn't installed.
50
- .pipe(Effect.annotateLogs("correlationId", correlationId || "unknown"))
51
- .pipe(Effect.annotateLogs("requestId", _currentRequestId));
52
- if (pluginName) e = e.pipe(Effect.annotateLogs("plugin", pluginName));
53
- return e
54
- .pipe(Effect.provideService(requestContextTag, { correlationId: correlationId || "unknown" }))
55
- .pipe(Effect.runPromise);
56
- }
57
-
58
- function extractCorrelationId(records) {
59
- const first = records[0];
60
- if (!first) return undefined;
61
- const body = first.body;
62
- if (body == null) return undefined;
63
- try {
64
- const parsed = JSON.parse(body);
65
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
66
- const meta = parsed.meta;
67
- if (meta && typeof meta === "object" && !Array.isArray(meta)) {
68
- const cid = meta.correlationId;
69
- if (typeof cid === "string") return cid;
70
- }
71
- }
72
- } catch (_) {}
73
- return undefined;
74
- }
50
+ // This Lambda hosts one plugin's StateChangeSlices behind a composite handler
51
+ // that routes per command TAG *inside* the stream — so the dispatch boundary
52
+ // names the runtime group, not the individual slice. `Kind(Name)` matches the
53
+ // shape every other comp uses, and the plugin name resolves through
54
+ // LogPrefix.
55
+ const dcbComp = (plugin) => `DcbCommandTopicRuntime(${plugin})`;
75
56
 
76
57
  // Exported for tests: build the [sqsHandler, cmdGenHandler] pair from an
77
58
  // already-parsed `HANDLER_CONFIG` shape, with an injectable module loader.
@@ -204,7 +185,10 @@ export async function buildHandlersForConfig(config, opts = {}) {
204
185
  return generateCommand({ ...event, meta, identity });
205
186
  };
206
187
 
207
- return [sqsHandler, cmdGenHandler];
188
+ // Third element is additive — existing callers (the integration tests) keep
189
+ // destructuring the first two. No deploy-time `plugin` fragment needed here:
190
+ // this Lambda serves exactly one plugin and HANDLER_CONFIG already names it.
191
+ return [sqsHandler, cmdGenHandler, dcbComp(config.pluginName), config.pluginName];
208
192
  }
209
193
 
210
194
  async function buildHandler() {
@@ -212,7 +196,7 @@ async function buildHandler() {
212
196
  // Importing this module without HANDLER_CONFIG (e.g. from a test that drives
213
197
  // `buildHandlersForConfig` directly) must not crash module evaluation.
214
198
  // Lambda always sets HANDLER_CONFIG; the production path is unchanged.
215
- if (!configStr) return [null, null];
199
+ if (!configStr) return [null, null, undefined, undefined];
216
200
  const config = JSON.parse(configStr);
217
201
  return buildHandlersForConfig(config, { loadModule: dynamicImport });
218
202
  }
@@ -220,21 +204,34 @@ async function buildHandler() {
220
204
  const initPromise = buildHandler();
221
205
 
222
206
  export async function handler(event, context) {
223
- _currentRequestId = context?.awsRequestId || "unknown";
224
- const [sqsHandler, cmdGenHandler] = await initPromise;
207
+ setRequestId(context?.awsRequestId);
208
+ const [sqsHandler, cmdGenHandler, comp, plugin] = await initPromise;
225
209
 
226
210
  // Route 1: AppSync direct invocation — payload carries the CommandGenerator.payload
227
211
  // shape (`{command, arguments, meta, identity?}`).
228
212
  if (event.command != null && event.arguments != null) {
229
213
  log.debug("AppSync direct invocation (" + DISPATCH_MODE + ")", { comp: "DcbCommandTopicRuntime" });
230
- const outcome = await runEffect(undefined, cmdGenHandler(event));
214
+ // Direct invoke: the envelope is on `event.meta`, and there is no send time
215
+ // or receive count on this path.
216
+ const outcome = await runEffect(cmdGenHandler(event), {
217
+ correlationId: event.meta?.correlationId,
218
+ causationId: event.meta?.causationId,
219
+ comp,
220
+ plugin,
221
+ });
231
222
  return commandOutcomeToJson(outcome);
232
223
  }
233
224
 
234
225
  // Route 2: SQS CommandTopic events
235
226
  const records = event.Records || [];
236
- const correlationId = extractCorrelationId(records);
237
227
  log.debug("processing " + records.length.toString() + " record(s)", { comp: "DcbCommandTopicRuntime" });
238
- await runEffect(correlationId, sqsHandler(event, context));
228
+ await runEffect(sqsHandler(event, context), {
229
+ correlationId: extractMetaField(records, "correlationId"),
230
+ causationId: extractMetaField(records, "causationId"),
231
+ comp,
232
+ plugin,
233
+ timestamp: extractSentTimestamp(records),
234
+ retryCount: extractRetryCount(records),
235
+ });
239
236
  return "";
240
237
  }
@@ -67,11 +67,16 @@ let forEventCollector: ReventlessCore.Runtime.forEventCollector<
67
67
  let mappingsModule =
68
68
  info.mappingsModulePath->JSON.stringifyAny->Option.getOr(`""`)
69
69
 
70
+ // Same deploy-time log attribution as the shared-runtime builder: the
71
+ // entry point annotates every invocation with these, and the `comp` string
72
+ // must match the ReScript dispatch boundary's `EventCollector(<name>)`.
73
+ let attribution = Util_LogAttribution.fragments(~comp=`EventCollector(${eventCollectorName})`)
74
+
70
75
  let handlerConfigOutput =
71
76
  Pulumi.Output.all2((info.queryDbTableName, sourceUrns))
72
77
  ->Pulumi.Output.apply(((tableName, urns)) => {
73
78
  let sourceUrn = urns->Array.getUnsafe(0)
74
- `{"handlers":[{"specModule":${specModule},"mappingsModule":${mappingsModule},"queryDbTableName":"${tableName}","sourceUrn":"${sourceUrn}"}]}`
79
+ `{"handlers":[{"specModule":${specModule},"mappingsModule":${mappingsModule},"queryDbTableName":"${tableName}","sourceUrn":"${sourceUrn}"${attribution}}]}`
75
80
  })
76
81
 
77
82
  let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
@@ -93,6 +98,7 @@ let forEventCollector: ReventlessCore.Runtime.forEventCollector<
93
98
  let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
94
99
  ~name,
95
100
  ~unitKind=ReventlessCore.Monitoring.EventCollector,
101
+ ~componentKind=ReventlessCore.ComponentType.EventCollector,
96
102
  ~code,
97
103
  ~sourceCodeHash,
98
104
  ~envVars,
@@ -8,6 +8,7 @@ import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/co
8
8
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
9
9
  import * as ComponentType$ReventlessCore from "@reventlessdev/reventless-core/src/ComponentType.res.mjs";
10
10
  import * as EventCollector$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventCollector/EventCollector.res.mjs";
11
+ import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
11
12
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
12
13
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
13
14
 
@@ -46,12 +47,13 @@ function forEventCollector(param, eventTopics, resources, memorySizeOpt, timeout
46
47
  };
47
48
  let specModule = Stdlib_Option.getOr(JSON.stringify(info.specModulePath), `""`);
48
49
  let mappingsModule = Stdlib_Option.getOr(JSON.stringify(info.mappingsModulePath), `""`);
50
+ let attribution = Util_LogAttribution$ReventlessAws.fragments(`EventCollector(` + eventCollectorName + `)`);
49
51
  let handlerConfigOutput = Pulumi.all([
50
52
  info.queryDbTableName,
51
53
  sourceUrns
52
54
  ]).apply(param => {
53
55
  let sourceUrn = param[1][0];
54
- return `{"handlers":[{"specModule":` + specModule + `,"mappingsModule":` + mappingsModule + `,"queryDbTableName":"` + param[0] + `","sourceUrn":"` + sourceUrn + `"}]}`;
56
+ return `{"handlers":[{"specModule":` + specModule + `,"mappingsModule":` + mappingsModule + `,"queryDbTableName":"` + param[0] + `","sourceUrn":"` + sourceUrn + `"` + attribution + `}]}`;
55
57
  });
56
58
  let envVars = {};
57
59
  envVars["HANDLER_CONFIG"] = handlerConfigOutput;
@@ -61,7 +63,7 @@ function forEventCollector(param, eventTopics, resources, memorySizeOpt, timeout
61
63
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(specPkg);
62
64
  packageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(mappingsPkg);
63
65
  let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ReadModelEntryPoint.mjs", packageDirs, undefined);
64
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "EventCollector", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts);
66
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "EventCollector", "EventCollector", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts);
65
67
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect(name, [{
66
68
  channel: channel,
67
69
  eventTopics: eventTopics,
@@ -44,6 +44,13 @@ let registerReadModel = (
44
44
 
45
45
  type storedSpec = {
46
46
  componentName: string,
47
+ /** The event collector's own resource name (`CustomersReadModel`), as opposed
48
+ to `componentName` (its parent read model, `Customers`). Source of the
49
+ `comp` baked into HANDLER_CONFIG — the deployed entry point annotates logs
50
+ with it, so it must match the ReScript dispatch boundary's
51
+ `EventCollector(<name>)` exactly (see
52
+ docs/plans/entrypoint-dispatch-parity-and-latency-fields.md). */
53
+ eventCollectorName: string,
47
54
  parentResource: Pulumi.Resource.t,
48
55
  sourceUrns: Pulumi.Output.t<array<string>>,
49
56
  channelSpec: ReventlessCore.EventCollector_Adapter.channelSpec<
@@ -93,6 +100,7 @@ let forEventCollector: ReventlessCore.Runtime.forEventCollector<
93
100
  storedSpecs
94
101
  ->Array.push({
95
102
  componentName: parentName,
103
+ eventCollectorName,
96
104
  parentResource,
97
105
  sourceUrns,
98
106
  channelSpec: {channel, eventTopics, resources},
@@ -210,6 +218,15 @@ let finish = () =>
210
218
  `,"stateTopicName":${topic->JSON.Encode.string->JSON.stringify}`
211
219
  }
212
220
  : ""
221
+ // Log attribution baked at deploy time: this Lambda hosts every read
222
+ // model, so the shell needs a per-handler `comp` to keep their log
223
+ // lines separable, and a per-handler `plugin` because the shared
224
+ // Lambda's own name carries no single plugin identity. Both are
225
+ // resolved here — the registry LogPrefix consults is a deploy-time
226
+ // structure, empty inside the Lambda.
227
+ let attribution = Util_LogAttribution.fragments(
228
+ ~comp=`EventCollector(${spec.eventCollectorName})`,
229
+ )
213
230
  let handlerJson =
214
231
  Pulumi.Output.all4((
215
232
  info.queryDbTableName,
@@ -221,7 +238,7 @@ let finish = () =>
221
238
  // No stream resource (Postgres-backed source) → dispatch on the
222
239
  // feed queue's ARN instead of a stream URN.
223
240
  let sourceUrn = urns->Array.get(0)->Option.getOr(feedArn)
224
- `{"specModule":${specModule},"mappingsModule":${mappingsModule},"queryDbTableName":"${tableName}","sourceUrn":"${sourceUrn}"${pgFragment}${stateTopicFragment}}`
241
+ `{"specModule":${specModule},"mappingsModule":${mappingsModule},"queryDbTableName":"${tableName}","sourceUrn":"${sourceUrn}"${attribution}${pgFragment}${stateTopicFragment}}`
225
242
  })
226
243
  let _ = handlerOutputs->Array.push(handlerJson)
227
244
  | None =>
@@ -284,6 +301,7 @@ let finish = () =>
284
301
  let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
285
302
  ~name="AllReadModels",
286
303
  ~unitKind=ReventlessCore.Monitoring.EventCollector,
304
+ ~componentKind=ReventlessCore.ComponentType.EventCollector,
287
305
  ~code,
288
306
  ~sourceCodeHash,
289
307
  ~envVars,
@@ -17,6 +17,7 @@ import * as QueryDbBackend$ReventlessAws from "../QueryDb/QueryDbBackend.res.mjs
17
17
  import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.mjs";
18
18
  import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
19
19
  import * as PgProjectionFeed$ReventlessAws from "../Postgres/PgProjectionFeed.res.mjs";
20
+ import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
20
21
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
21
22
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
22
23
 
@@ -65,6 +66,7 @@ function forEventCollector(param, eventTopics, resources, memorySizeOpt, timeout
65
66
  let sourceUrns = Pulumi.all(Component$ReventlessCore.outputs(eventCollector).resources.map(param => param.urn));
66
67
  storedSpecs.push({
67
68
  componentName: parentName,
69
+ eventCollectorName: eventCollectorName,
68
70
  parentResource: parentResource,
69
71
  sourceUrns: sourceUrns,
70
72
  channelSpec: {
@@ -128,6 +130,7 @@ function finish() {
128
130
  } else {
129
131
  stateTopicFragment = "";
130
132
  }
133
+ let attribution = Util_LogAttribution$ReventlessAws.fragments(`EventCollector(` + spec.eventCollectorName + `)`);
131
134
  let handlerJson = Pulumi.all([
132
135
  info.queryDbTableName,
133
136
  spec.sourceUrns,
@@ -135,7 +138,7 @@ function finish() {
135
138
  handlerPgFragment
136
139
  ]).apply(param => {
137
140
  let sourceUrn = Stdlib_Option.getOr(param[1][0], param[2]);
138
- return `{"specModule":` + specModule + `,"mappingsModule":` + mappingsModule + `,"queryDbTableName":"` + param[0] + `","sourceUrn":"` + sourceUrn + `"` + param[3] + stateTopicFragment + `}`;
141
+ return `{"specModule":` + specModule + `,"mappingsModule":` + mappingsModule + `,"queryDbTableName":"` + param[0] + `","sourceUrn":"` + sourceUrn + `"` + attribution + param[3] + stateTopicFragment + `}`;
139
142
  });
140
143
  handlerOutputs.push(handlerJson);
141
144
  });
@@ -158,7 +161,7 @@ function finish() {
158
161
  subnetIds: qdbSelection.subnetIds,
159
162
  securityGroupIds: [sgId]
160
163
  })) : undefined;
161
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllReadModels", "EventCollector", match$1.code, match$1.sourceCodeHash, envVars, match[0], match[1], undefined, undefined, undefined, undefined, vpcConfig, opts);
164
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllReadModels", "EventCollector", "EventCollector", match$1.code, match$1.sourceCodeHash, envVars, match[0], match[1], undefined, undefined, undefined, undefined, vpcConfig, opts);
162
165
  if (qdbSelection !== undefined && anyPgBacked) {
163
166
  qdbSelection.connectionConfig.apply(cc => new (Aws.iam.RolePolicy)("AllReadModels-pgSecret", {
164
167
  policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, "AllReadModels-pgSecretPolicy", [{
@@ -2,10 +2,17 @@
2
2
  // At cold start: reads HANDLER_CONFIG, dynamically imports Target Spec and Mappings,
3
3
  // wires EventMapper_Callback.MakeCounterHandler + MakeEventCollectorHandler.
4
4
 
5
- import * as Effect from "effect/Effect";
6
5
  import { $$String as IdString } from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
7
- import { patchSpecId, makeQueueRef, log, pluginName } from "./HandlerFactoryHelpers.mjs";
8
- import { tag as requestContextTag } from "@reventlessdev/reventless-core/src/RequestContext.res.mjs";
6
+ import {
7
+ patchSpecId,
8
+ makeQueueRef,
9
+ log,
10
+ runEffect,
11
+ setRequestId,
12
+ extractMetaField,
13
+ extractSentTimestamp,
14
+ extractRetryCount,
15
+ } from "./HandlerFactoryHelpers.mjs";
9
16
  import { MakeCounterHandler, MakeEventCollectorHandler } from "@reventlessdev/reventless-core/src/components/EventMapper/EventMapper_Callback.res.mjs";
10
17
  import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
11
18
  import { handleStreamEvent } from "@reventlessdev/reventless-aws/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs";
@@ -26,22 +33,6 @@ function patchMappingsSourceIds(mappingsModule) {
26
33
  };
27
34
  }
28
35
 
29
- // Set at handler entry; read by runEffect to tag logs with the Lambda request id.
30
- let _currentRequestId = "unknown";
31
-
32
- function runEffect(correlationId, effect) {
33
- let e = effect
34
- // Promote correlationId/requestId/plugin to top-level JSON log fields —
35
- // decoded by EffectLogger.install() from Effect log annotations. Harmless
36
- // no-op if the unified logger isn't installed.
37
- .pipe(Effect.annotateLogs("correlationId", correlationId || "unknown"))
38
- .pipe(Effect.annotateLogs("requestId", _currentRequestId));
39
- if (pluginName) e = e.pipe(Effect.annotateLogs("plugin", pluginName));
40
- return e
41
- .pipe(Effect.provideService(requestContextTag, { correlationId: correlationId || "unknown" }))
42
- .pipe(Effect.runPromise);
43
- }
44
-
45
36
  function groupBySource(records) {
46
37
  const dict = {};
47
38
  records.forEach(record => {
@@ -77,20 +68,31 @@ async function buildHandler() {
77
68
  commonEventsHandler: counterHandler.commonEventsHandler,
78
69
  });
79
70
 
80
- return (event, context) => handleStreamEvent(eventCollectorHandler.handleJsonEvents, event, context);
71
+ return {
72
+ streamHandler: (event, context) =>
73
+ handleStreamEvent(eventCollectorHandler.handleJsonEvents, event, context),
74
+ // The mapper is named after the target it maps into.
75
+ comp: `EventMapper(${patchedTarget.name})`,
76
+ };
81
77
  }
82
78
 
83
79
  const initPromise = buildHandler();
84
80
 
85
81
  export async function handler(event, context) {
86
- _currentRequestId = context?.awsRequestId || "unknown";
87
- const streamHandler = await initPromise;
82
+ setRequestId(context?.awsRequestId);
83
+ const { streamHandler, comp } = await initPromise;
88
84
  const records = event.Records || [];
89
85
  const grouped = groupBySource(records);
90
86
 
91
87
  await Promise.all(Object.entries(grouped).map(async ([arn, subRecords]) => {
92
88
  log.debug("processing " + arn, { comp: "EventMapperRuntime" });
93
- await runEffect(undefined, streamHandler({ Records: subRecords }, context));
89
+ await runEffect(streamHandler({ Records: subRecords }, context), {
90
+ correlationId: extractMetaField(subRecords, "correlationId"),
91
+ causationId: extractMetaField(subRecords, "causationId"),
92
+ comp,
93
+ timestamp: extractSentTimestamp(subRecords),
94
+ retryCount: extractRetryCount(subRecords),
95
+ });
94
96
  }));
95
97
 
96
98
  return "";