@reventlessdev/reventless-core 3.0.0-alpha.172 → 3.0.0-alpha.173

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.173 (2026-07-20)
7
+
8
+ ### Features
9
+
10
+ * **core:** annotate comp + causationId at the dispatch boundary ([ef6d56d](https://github.com/ReventlessDev/reventless-core/commit/ef6d56d0104dbdb21b7cfbb8f3b2b1ead9787595))
11
+
12
+
6
13
  # 3.0.0-alpha.172 (2026-07-17)
7
14
 
8
15
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.172",
3
+ "version": "3.0.0-alpha.173",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -30,15 +30,15 @@
30
30
  "uuid": "^13.0.0",
31
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.29",
32
32
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
33
+ "@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
33
34
  "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
34
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
35
35
  "@reventlessdev/rescript-node-streams": "1.1.0-alpha.13",
36
- "@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
37
- "@reventlessdev/reventless-infra": "3.0.0-alpha.102",
38
- "@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
39
- "@reventlessdev/reventless-interop": "3.0.0-alpha.28",
36
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
40
37
  "@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
41
- "@reventlessdev/reventless-spec": "3.0.0-alpha.79"
38
+ "@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
39
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.102",
40
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.79",
41
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.28"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.0",
@@ -14,14 +14,42 @@
14
14
  // In tests:
15
15
  // ->Effect.provideService(RequestContext.tag, RequestContext.test())
16
16
 
17
+ // `causationId` = the direct-parent msgId of the message that triggered this
18
+ // invocation (Message.deriveMeta propagates it); reconstructs the parent → child
19
+ // causal chain within one `correlationId`. `component` / `pluginName` identify the
20
+ // component and plugin handling the invocation — mirror the `comp` / `plugin` log
21
+ // fields so a consumer of this service sees the same attribution the logs carry.
22
+ // All three are optional: absent in `.test()` and wherever the dispatch site has
23
+ // no value to supply.
17
24
  type t = {
18
25
  correlationId: string,
26
+ causationId?: string,
27
+ component?: string,
28
+ pluginName?: string,
19
29
  identity: Reventless.Identity.t,
20
30
  claims: dict<string>,
21
31
  }
22
32
 
23
33
  let tag: Context.tag<t> = Context.genericTag("reventless/RequestContext")
24
34
 
35
+ // Real dispatch-boundary constructor. Use this at runtime dispatch points
36
+ // (see Runtime.runEffect); `test` is test-only.
37
+ let make = (
38
+ ~correlationId,
39
+ ~causationId=?,
40
+ ~component=?,
41
+ ~pluginName=?,
42
+ ~identity=Reventless.Identity.anonymous,
43
+ ~claims=Dict.make(),
44
+ ): t => {
45
+ correlationId,
46
+ causationId: ?causationId,
47
+ component: ?component,
48
+ pluginName: ?pluginName,
49
+ identity,
50
+ claims,
51
+ }
52
+
25
53
  let getClaim = (ctx: t, key: string): option<string> =>
26
54
  ctx.claims->Dict.get(key)
27
55
 
@@ -5,6 +5,19 @@ import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/I
5
5
 
6
6
  let tag = Context.GenericTag("reventless/RequestContext");
7
7
 
8
+ function make(correlationId, causationId, component, pluginName, identityOpt, claimsOpt) {
9
+ let identity = identityOpt !== undefined ? identityOpt : Identity$Reventless.anonymous;
10
+ let claims = claimsOpt !== undefined ? claimsOpt : ({});
11
+ return {
12
+ correlationId: correlationId,
13
+ causationId: causationId,
14
+ component: component,
15
+ pluginName: pluginName,
16
+ identity: identity,
17
+ claims: claims
18
+ };
19
+ }
20
+
8
21
  function getClaim(ctx, key) {
9
22
  return ctx.claims[key];
10
23
  }
@@ -12,11 +25,9 @@ function getClaim(ctx, key) {
12
25
  function withClaim(ctx, key, value) {
13
26
  let next = Object.fromEntries(Object.entries(ctx.claims));
14
27
  next[key] = value;
15
- return {
16
- correlationId: ctx.correlationId,
17
- identity: ctx.identity,
18
- claims: next
19
- };
28
+ let newrecord = {...ctx};
29
+ newrecord.claims = next;
30
+ return newrecord;
20
31
  }
21
32
 
22
33
  function test(correlationIdOpt, identityOpt, claimsOpt) {
@@ -32,6 +43,7 @@ function test(correlationIdOpt, identityOpt, claimsOpt) {
32
43
 
33
44
  export {
34
45
  tag,
46
+ make,
35
47
  getClaim,
36
48
  withClaim,
37
49
  test,
@@ -31,26 +31,21 @@ module Make = (
31
31
  let commandTopicHandlers: dict<effectHandler> = Dict.make()
32
32
  let eventCollectorHandlers: dict<array<effectHandler>> = Dict.make()
33
33
 
34
- let runEffect = (~correlationId=?, effect) => {
35
- let cid = correlationId->Option.getOr("unknown")
36
- effect
37
- // Annotate the request scope so every EffectLogger call inside the handler
38
- // carries correlationId as a top-level JSON field (decoded in install()).
39
- ->Effect.annotateLogs("correlationId", cid)
40
- ->Effect.provideService(RequestContext.tag, RequestContext.test(~correlationId=cid))
41
- ->Effect.runPromise
42
- }
43
-
44
34
  let aggregateHandler = aggregateName =>
45
35
  async (event: RuntimeEnvironment.event, context) => {
46
36
  let correlationId = event->RuntimeEnvironment.extractCorrelationId
37
+ let causationId = event->RuntimeEnvironment.extractCausationId
47
38
  let comp = `AggregateRuntime(${aggregateName})`
39
+ // Shared dispatch helper (Runtime.runEffect) annotates correlationId / comp /
40
+ // causationId onto the invocation so the application handler's own log lines
41
+ // carry them without re-logging by hand.
42
+ let runEffect = effect => Runtime.runEffect(~correlationId?, ~causationId?, ~comp, effect)
48
43
  switch event->CommandGenerator.metaInfo {
49
44
  | Some(info) =>
50
45
  switch commandGeneratorHandlers->Dict.get(info) {
51
46
  | Some(handler) =>
52
47
  EffectLogger.logDebug(~comp, `found CommandGenerator handler for ${info}`)->Effect.runSync
53
- let _ = await runEffect(~correlationId?, handler(event->CommandGenerator.asPayload, context))
48
+ let _ = await runEffect(handler(event->CommandGenerator.asPayload, context))
54
49
  | None =>
55
50
  let available = commandGeneratorHandlers->Dict.keysToArray->Array.join(",")
56
51
  EffectLogger.logWarn(
@@ -66,7 +61,7 @@ module Make = (
66
61
  switch commandTopicHandlers->Dict.get(urn) {
67
62
  | Some(handler) =>
68
63
  EffectLogger.logDebug(~comp, `found CommandTopic handler for ${urn}`)->Effect.runSync
69
- await runEffect(~correlationId?, handler(event, context))
64
+ await runEffect(handler(event, context))
70
65
  | None =>
71
66
  switch eventCollectorHandlers->Dict.get(urn) {
72
67
  | Some(handlers) =>
@@ -76,7 +71,7 @@ module Make = (
76
71
  `found ${count} EventCollector handler(s) for ${urn}`,
77
72
  )->Effect.runSync
78
73
  let _ = await handlers
79
- ->Array.map(handler => runEffect(~correlationId?, handler(event, context)))
74
+ ->Array.map(handler => runEffect(handler(event, context)))
80
75
  ->Promise.all
81
76
  | None =>
82
77
  let available =
@@ -6,9 +6,9 @@ import * as Effect from "effect/Effect";
6
6
  import * as Pulumi from "@pulumi/pulumi";
7
7
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
8
  import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
9
+ import * as Runtime$ReventlessCore from "./Runtime.res.mjs";
9
10
  import * as Component$ReventlessCore from "../../components/Component.res.mjs";
10
11
  import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
11
- import * as RequestContext$ReventlessCore from "../../RequestContext.res.mjs";
12
12
  import * as CommandGenerator$ReventlessCore from "../../components/CommandGenerator/CommandGenerator.res.mjs";
13
13
 
14
14
  function Make(RuntimeEnvironment) {
@@ -18,19 +18,17 @@ function Make(RuntimeEnvironment) {
18
18
  let commandGeneratorHandlers = {};
19
19
  let commandTopicHandlers = {};
20
20
  let eventCollectorHandlers = {};
21
- let runEffect = (correlationId, effect) => {
22
- let cid = Stdlib_Option.getOr(correlationId, "unknown");
23
- return Effect.runPromise(Effect.provideService(Effect.annotateLogs(effect, "correlationId", cid), RequestContext$ReventlessCore.tag, RequestContext$ReventlessCore.test(cid, undefined, undefined)));
24
- };
25
21
  let aggregateHandler = aggregateName => (async (event, context) => {
26
22
  let correlationId = RuntimeEnvironment.extractCorrelationId(event);
23
+ let causationId = RuntimeEnvironment.extractCausationId(event);
27
24
  let comp = `AggregateRuntime(` + aggregateName + `)`;
25
+ let runEffect = effect => Runtime$ReventlessCore.runEffect(correlationId, causationId, comp, undefined, effect);
28
26
  let info = CommandGenerator$ReventlessCore.metaInfo(event);
29
27
  if (info !== undefined) {
30
28
  let handler = commandGeneratorHandlers[info];
31
29
  if (handler !== undefined) {
32
30
  Effect.runSync(EffectLogger$ReventlessCore.logDebug(comp, undefined, `found CommandGenerator handler for ` + info));
33
- await runEffect(correlationId, handler(event, context));
31
+ await runEffect(handler(event, context));
34
32
  return;
35
33
  }
36
34
  let available = Object.keys(commandGeneratorHandlers).join(",");
@@ -42,13 +40,13 @@ function Make(RuntimeEnvironment) {
42
40
  let handler = commandTopicHandlers[urn];
43
41
  if (handler !== undefined) {
44
42
  Effect.runSync(EffectLogger$ReventlessCore.logDebug(comp, undefined, `found CommandTopic handler for ` + urn));
45
- return await runEffect(correlationId, handler(event, context));
43
+ return await runEffect(handler(event, context));
46
44
  }
47
45
  let handlers = eventCollectorHandlers[urn];
48
46
  if (handlers !== undefined) {
49
47
  let count = handlers.length.toString();
50
48
  Effect.runSync(EffectLogger$ReventlessCore.logDebug(comp, undefined, `found ` + count + ` EventCollector handler(s) for ` + urn));
51
- await Promise.all(handlers.map(handler => runEffect(correlationId, handler(event, context))));
49
+ await Promise.all(handlers.map(handler => runEffect(handler(event, context))));
52
50
  return;
53
51
  }
54
52
  let available = Object.keys(commandTopicHandlers).concat(Object.keys(eventCollectorHandlers)).join(",");
@@ -171,7 +169,6 @@ function Make(RuntimeEnvironment) {
171
169
  commandGeneratorHandlers: commandGeneratorHandlers,
172
170
  commandTopicHandlers: commandTopicHandlers,
173
171
  eventCollectorHandlers: eventCollectorHandlers,
174
- runEffect: runEffect,
175
172
  aggregateHandler: aggregateHandler,
176
173
  getRuntimeSpec: getRuntimeSpec,
177
174
  setRuntimeSpec: setRuntimeSpec,
@@ -23,10 +23,17 @@ module Make = (
23
23
  commandGenerator,
24
24
  ) => {
25
25
  let resource = commandGenerator->Component.toPulumiResource
26
+ let name = resource.name->ComponentType.nameOpt(CommandGenerator.componentType)
26
27
  let runtime = RuntimeEnvironment.make(
27
- ~name=resource.name->ComponentType.nameOpt(CommandGenerator.componentType),
28
+ ~name,
28
29
  ~handler=handler->Pulumi.Output.apply(handler =>
29
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
30
+ handler
31
+ ->RuntimeEnvironment.asEffectHandler
32
+ ->Runtime.runEffectHandler(
33
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
34
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
35
+ ~comp=name,
36
+ )
30
37
  ),
31
38
  ~memorySize,
32
39
  ~timeout,
@@ -44,10 +51,17 @@ module Make = (
44
51
  commandTopic,
45
52
  ) => {
46
53
  let resource = commandTopic->Component.toPulumiResource
54
+ let name = resource.name->ComponentType.nameOpt(CommandTopic.componentType)
47
55
  let runtime = RuntimeEnvironment.make(
48
- ~name=resource.name->ComponentType.nameOpt(CommandTopic.componentType),
56
+ ~name,
49
57
  ~handler=handler->Pulumi.Output.apply(handler =>
50
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
58
+ handler
59
+ ->RuntimeEnvironment.asEffectHandler
60
+ ->Runtime.runEffectHandler(
61
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
62
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
63
+ ~comp=name,
64
+ )
51
65
  ),
52
66
  ~memorySize,
53
67
  ~timeout,
@@ -72,7 +86,13 @@ module Make = (
72
86
  let runtime = RuntimeEnvironment.make(
73
87
  ~name,
74
88
  ~handler=handler->Pulumi.Output.apply(handler =>
75
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
89
+ handler
90
+ ->RuntimeEnvironment.asEffectHandler
91
+ ->Runtime.runEffectHandler(
92
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
93
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
94
+ ~comp=name,
95
+ )
76
96
  ),
77
97
  ~memorySize,
78
98
  ~timeout,
@@ -13,7 +13,8 @@ function Make(RuntimeEnvironment) {
13
13
  let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
14
14
  let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
15
15
  let resource = Component$ReventlessCore.toPulumiResource(commandGenerator);
16
- return connect(RuntimeEnvironment.make(ComponentType$ReventlessCore.nameOpt(resource.__name, CommandGenerator$ReventlessCore.componentType), handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
16
+ let name = ComponentType$ReventlessCore.nameOpt(resource.__name, CommandGenerator$ReventlessCore.componentType);
17
+ return connect(RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
17
18
  parent: resource
18
19
  }));
19
20
  };
@@ -21,7 +22,8 @@ function Make(RuntimeEnvironment) {
21
22
  let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
22
23
  let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
23
24
  let resource = Component$ReventlessCore.toPulumiResource(commandTopic);
24
- return connect(RuntimeEnvironment.make(ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType), handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
25
+ let name = ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType);
26
+ return connect(RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
25
27
  parent: resource
26
28
  }));
27
29
  };
@@ -34,7 +36,7 @@ function Make(RuntimeEnvironment) {
34
36
  let opts = {
35
37
  parent: opts_parent
36
38
  };
37
- let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, opts);
39
+ let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, opts);
38
40
  EventCollectorChannel.connect(name, [{
39
41
  channel: eventCollector.channel,
40
42
  eventTopics: eventTopics,
@@ -26,7 +26,13 @@ module Make = (
26
26
  let runtime = RuntimeEnvironment.make(
27
27
  ~name,
28
28
  ~handler=handler->Pulumi.Output.apply(handler =>
29
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
29
+ handler
30
+ ->RuntimeEnvironment.asEffectHandler
31
+ ->Runtime.runEffectHandler(
32
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
33
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
34
+ ~comp=name,
35
+ )
30
36
  ),
31
37
  ~memorySize,
32
38
  ~timeout,
@@ -16,7 +16,7 @@ function Make(RuntimeEnvironment) {
16
16
  let opts = {
17
17
  parent: opts_parent
18
18
  };
19
- let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
19
+ let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
20
20
  parent: resource
21
21
  });
22
22
  EventCollectorChannel.connect(name, [{
@@ -35,20 +35,15 @@ module Make = (
35
35
  })
36
36
  type effectHandler = Runtime.effectHandler<RuntimeEnvironment.event, context, unit, string>
37
37
  let eventCollectorHandlers: dict<array<effectHandler>> = Dict.make()
38
- let runEffect = (~correlationId=?, effect) => {
39
- let cid = correlationId->Option.getOr("unknown")
40
- effect
41
- // Annotate the request scope so every EffectLogger call inside the handler
42
- // carries correlationId as a top-level JSON field (decoded in install()).
43
- ->Effect.annotateLogs("correlationId", cid)
44
- ->Effect.provideService(RequestContext.tag, RequestContext.test(~correlationId=cid))
45
- ->Effect.runPromise
46
- }
47
38
 
48
39
  let eventCollectorHandler = parentName =>
49
40
  async (event: RuntimeEnvironment.event, context) => {
50
41
  let correlationId = event->RuntimeEnvironment.extractCorrelationId
42
+ let causationId = event->RuntimeEnvironment.extractCausationId
51
43
  let comp = `EventCollectorRuntime(${parentName})`
44
+ // Shared dispatch helper annotates correlationId / comp / causationId so the
45
+ // application handler's own log lines carry them (see Runtime.runEffect).
46
+ let runEffect = effect => Runtime.runEffect(~correlationId?, ~causationId?, ~comp, effect)
52
47
  let _ = await event
53
48
  ->RuntimeEnvironment.groupBySource
54
49
  ->Dict.toArray
@@ -61,7 +56,7 @@ module Make = (
61
56
  `found ${count} handler(s) for ${urn}`,
62
57
  )->Effect.runSync
63
58
  let _ = await handlers
64
- ->Array.map(handler => runEffect(~correlationId?, handler(event, context)))
59
+ ->Array.map(handler => runEffect(handler(event, context)))
65
60
  ->Promise.all
66
61
  | None => EffectLogger.logWarn(~comp, `no handler found: ${urn}`)->Effect.runSync
67
62
  }
@@ -6,9 +6,9 @@ import * as Pulumi from "@pulumi/pulumi";
6
6
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
7
7
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
8
8
  import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
9
+ import * as Runtime$ReventlessCore from "./Runtime.res.mjs";
9
10
  import * as Component$ReventlessCore from "../../components/Component.res.mjs";
10
11
  import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
11
- import * as RequestContext$ReventlessCore from "../../RequestContext.res.mjs";
12
12
 
13
13
  function Make(RuntimeEnvironment) {
14
14
  return EventCollectorChannel => {
@@ -29,6 +29,7 @@ function Make(RuntimeEnvironment) {
29
29
  let eventCollectorHandlers = {};
30
30
  let eventCollectorHandler = parentName => (async (event, context) => {
31
31
  let correlationId = RuntimeEnvironment.extractCorrelationId(event);
32
+ let causationId = RuntimeEnvironment.extractCausationId(event);
32
33
  let comp = `EventCollectorRuntime(` + parentName + `)`;
33
34
  await Promise.all(Object.entries(RuntimeEnvironment.groupBySource(event)).map(async param => {
34
35
  let event = param[1];
@@ -41,8 +42,7 @@ function Make(RuntimeEnvironment) {
41
42
  Effect.runSync(EffectLogger$ReventlessCore.logDebug(comp, undefined, `found ` + count + ` handler(s) for ` + urn));
42
43
  await Promise.all(handlers.map(handler => {
43
44
  let effect = handler(event, context);
44
- let cid = Stdlib_Option.getOr(correlationId, "unknown");
45
- return Effect.runPromise(Effect.provideService(Effect.annotateLogs(effect, "correlationId", cid), RequestContext$ReventlessCore.tag, RequestContext$ReventlessCore.test(cid, undefined, undefined)));
45
+ return Runtime$ReventlessCore.runEffect(correlationId, causationId, comp, undefined, effect);
46
46
  }));
47
47
  }));
48
48
  });
@@ -22,10 +22,17 @@ module Make = (
22
22
  commandTopic,
23
23
  ) => {
24
24
  let resource = commandTopic->Component.toPulumiResource
25
+ let name = resource.name->ComponentType.nameOpt(CommandTopic.componentType)
25
26
  let runtime = RuntimeEnvironment.make(
26
- ~name=resource.name->ComponentType.nameOpt(CommandTopic.componentType),
27
+ ~name,
27
28
  ~handler=handler->Pulumi.Output.apply(handler =>
28
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
29
+ handler
30
+ ->RuntimeEnvironment.asEffectHandler
31
+ ->Runtime.runEffectHandler(
32
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
33
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
34
+ ~comp=name,
35
+ )
29
36
  ),
30
37
  ~memorySize,
31
38
  ~timeout,
@@ -11,7 +11,8 @@ function Make(RuntimeEnvironment) {
11
11
  let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
12
12
  let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
13
13
  let resource = Component$ReventlessCore.toPulumiResource(commandTopic);
14
- return connect(RuntimeEnvironment.make(ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType), handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
14
+ let name = ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType);
15
+ return connect(RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
15
16
  parent: resource
16
17
  }));
17
18
  };
@@ -26,7 +26,13 @@ module Make = (
26
26
  let runtime = RuntimeEnvironment.make(
27
27
  ~name,
28
28
  ~handler=handler->Pulumi.Output.apply(handler =>
29
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
29
+ handler
30
+ ->RuntimeEnvironment.asEffectHandler
31
+ ->Runtime.runEffectHandler(
32
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
33
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
34
+ ~comp=name,
35
+ )
30
36
  ),
31
37
  ~memorySize,
32
38
  ~timeout,
@@ -70,10 +76,17 @@ module Make = (
70
76
  dcbCommandTopic,
71
77
  ) => {
72
78
  let resource = dcbCommandTopic->Component.toPulumiResource
79
+ let name = resource.name->ComponentType.nameOpt(CommandTopic.componentType)
73
80
  let runtime = RuntimeEnvironment.make(
74
- ~name=resource.name->ComponentType.nameOpt(CommandTopic.componentType),
81
+ ~name,
75
82
  ~handler=handler->Pulumi.Output.apply(handler =>
76
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
83
+ handler
84
+ ->RuntimeEnvironment.asEffectHandler
85
+ ->Runtime.runEffectHandler(
86
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
87
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
88
+ ~comp=name,
89
+ )
77
90
  ),
78
91
  ~memorySize,
79
92
  ~timeout,
@@ -18,7 +18,7 @@ function Make(RuntimeEnvironment) {
18
18
  let opts = {
19
19
  parent: opts_parent
20
20
  };
21
- let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
21
+ let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
22
22
  parent: resource
23
23
  });
24
24
  EventCollectorChannel.connect(name, [{
@@ -40,7 +40,8 @@ function Make(RuntimeEnvironment) {
40
40
  let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
41
41
  let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
42
42
  let resource = Component$ReventlessCore.toPulumiResource(dcbCommandTopic);
43
- return connect(RuntimeEnvironment.make(ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType), handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
43
+ let name = ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType);
44
+ return connect(RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
44
45
  parent: resource
45
46
  }));
46
47
  };
@@ -26,7 +26,13 @@ module Make = (
26
26
  let runtime = RuntimeEnvironment.make(
27
27
  ~name,
28
28
  ~handler=handler->Pulumi.Output.apply(handler =>
29
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
29
+ handler
30
+ ->RuntimeEnvironment.asEffectHandler
31
+ ->Runtime.runEffectHandler(
32
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
33
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
34
+ ~comp=name,
35
+ )
30
36
  ),
31
37
  ~memorySize,
32
38
  ~timeout,
@@ -70,10 +76,17 @@ module Make = (
70
76
  dcbCommandTopic,
71
77
  ) => {
72
78
  let resource = dcbCommandTopic->Component.toPulumiResource
79
+ let name = resource.name->ComponentType.nameOpt(CommandTopic.componentType)
73
80
  let runtime = RuntimeEnvironment.make(
74
- ~name=resource.name->ComponentType.nameOpt(CommandTopic.componentType),
81
+ ~name,
75
82
  ~handler=handler->Pulumi.Output.apply(handler =>
76
- handler->RuntimeEnvironment.asEffectHandler->Runtime.runEffectHandler
83
+ handler
84
+ ->RuntimeEnvironment.asEffectHandler
85
+ ->Runtime.runEffectHandler(
86
+ ~extractCorrelationId=RuntimeEnvironment.extractCorrelationId,
87
+ ~extractCausationId=RuntimeEnvironment.extractCausationId,
88
+ ~comp=name,
89
+ )
77
90
  ),
78
91
  ~memorySize,
79
92
  ~timeout,
@@ -18,7 +18,7 @@ function Make(RuntimeEnvironment) {
18
18
  let opts = {
19
19
  parent: opts_parent
20
20
  };
21
- let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
21
+ let runtime = RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
22
22
  parent: resource
23
23
  });
24
24
  EventCollectorChannel.connect(name, [{
@@ -40,7 +40,8 @@ function Make(RuntimeEnvironment) {
40
40
  let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
41
41
  let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
42
42
  let resource = Component$ReventlessCore.toPulumiResource(dcbCommandTopic);
43
- return connect(RuntimeEnvironment.make(ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType), handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
43
+ let name = ComponentType$ReventlessCore.nameOpt(resource.__name, CommandTopic$ReventlessCore.componentType);
44
+ return connect(RuntimeEnvironment.make(name, handler.apply(handler => Runtime$ReventlessCore.runEffectHandler(RuntimeEnvironment.extractCorrelationId, RuntimeEnvironment.extractCausationId, name, undefined, RuntimeEnvironment.asEffectHandler(handler))), memorySize, timeout, {
44
45
  parent: resource
45
46
  }));
46
47
  };
@@ -4,17 +4,70 @@ type effectHandler<'event, 'context, 'result, 'error> = (
4
4
  'context,
5
5
  ) => Effect.t<'result, 'error, unit>
6
6
 
7
- // Convert an effectHandler to an eventHandler by providing Logger, RequestContext, and running as promise.
8
- // Used at handler dispatch points (runtime builders) where Effect.runPromise is called.
9
- let runEffectHandler = (handler: effectHandler<'event, 'context, 'result, 'error>): eventHandler<
10
- 'event,
11
- 'context,
12
- 'result,
13
- > =>
14
- (event, ctx) =>
7
+ // ─── Dispatch-boundary annotation ────────────────────────────────────────────
8
+ // Single source of truth for what every handler invocation carries. Annotating
9
+ // here (rather than in each runtime builder) guarantees that every log line
10
+ // inside the handler — framework *and* application — surfaces the same
11
+ // `correlationId` / `comp` / `causationId` top-level JSON fields (EffectLogger
12
+ // decodes them), and that a populated RequestContext is provided. `comp` is what
13
+ // makes two components hosted in one runtime process separable purely by field;
14
+ // `causationId` reconstructs the parent → child chain within one `correlationId`.
15
+ let annotateInvocation = (
16
+ effect,
17
+ ~correlationId=?,
18
+ ~causationId=?,
19
+ ~comp=?,
20
+ ~pluginName=?,
21
+ ) => {
22
+ let cid = correlationId->Option.getOr("unknown")
23
+ let annotateOpt = (eff, key, value) =>
24
+ switch value {
25
+ | Some(v) => eff->Effect.annotateLogs(key, v)
26
+ | None => eff
27
+ }
28
+ effect
29
+ ->Effect.annotateLogs("correlationId", cid)
30
+ ->annotateOpt("comp", comp)
31
+ ->annotateOpt("causationId", causationId)
32
+ ->Effect.provideService(
33
+ RequestContext.tag,
34
+ RequestContext.make(~correlationId=cid, ~causationId?, ~component=?comp, ~pluginName?),
35
+ )
36
+ }
37
+
38
+ // Annotate + run. Used by the multi-component dispatchers (AllAggregates,
39
+ // AllEventCollectors) that loop over per-source handlers.
40
+ let runEffect = (~correlationId=?, ~causationId=?, ~comp=?, ~pluginName=?, effect) =>
41
+ effect
42
+ ->annotateInvocation(~correlationId?, ~causationId?, ~comp?, ~pluginName?)
43
+ ->Effect.runPromise
44
+
45
+ // Convert an effectHandler to an eventHandler at build time, annotating the
46
+ // invocation on each call. Used by the single-component-per-Lambda strategies
47
+ // (Micro / PerAggregate / Plugin / PerEventCollector / PerExtensionPoint), which
48
+ // pass the Environment's `extractCorrelationId` / `extractCausationId` and the
49
+ // component `comp` so their application handlers get the same fields as the
50
+ // multi-component path. Extractors default to none for generic use.
51
+ let runEffectHandler = (
52
+ ~extractCorrelationId: option<'event => option<string>>=?,
53
+ ~extractCausationId: option<'event => option<string>>=?,
54
+ ~comp=?,
55
+ ~pluginName=?,
56
+ handler: effectHandler<'event, 'context, 'result, 'error>,
57
+ ): eventHandler<'event, 'context, 'result> =>
58
+ (event, ctx) => {
59
+ let correlationId = switch extractCorrelationId {
60
+ | Some(f) => f(event)
61
+ | None => None
62
+ }
63
+ let causationId = switch extractCausationId {
64
+ | Some(f) => f(event)
65
+ | None => None
66
+ }
15
67
  handler(event, ctx)
16
- ->Effect.provideService(RequestContext.tag, RequestContext.test())
68
+ ->annotateInvocation(~correlationId?, ~causationId?, ~comp?, ~pluginName?)
17
69
  ->Effect.runPromise
70
+ }
18
71
 
19
72
  type environment<'parts> = {
20
73
  parts: 'parts,
@@ -36,6 +89,7 @@ module type Environment = {
36
89
  let make: environmentMaker<event, context, 'result, parts>
37
90
  let groupBySource: event => dict<event>
38
91
  let extractCorrelationId: event => option<string>
92
+ let extractCausationId: event => option<string>
39
93
  let asEventHandler: 'a => eventHandler<event, context, 'result>
40
94
  let asEffectHandler: 'a => effectHandler<event, context, 'result, 'error>
41
95
  }
@@ -1,10 +1,31 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
3
4
  import * as Effect from "effect/Effect";
4
5
  import * as RequestContext$ReventlessCore from "../../RequestContext.res.mjs";
5
6
 
6
- function runEffectHandler(handler) {
7
- return (event, ctx) => Effect.runPromise(Effect.provideService(handler(event, ctx), RequestContext$ReventlessCore.tag, RequestContext$ReventlessCore.test(undefined, undefined, undefined)));
7
+ function annotateInvocation(effect, correlationId, causationId, comp, pluginName) {
8
+ let cid = Stdlib_Option.getOr(correlationId, "unknown");
9
+ let annotateOpt = (eff, key, value) => {
10
+ if (value !== undefined) {
11
+ return Effect.annotateLogs(eff, key, value);
12
+ } else {
13
+ return eff;
14
+ }
15
+ };
16
+ return Effect.provideService(annotateOpt(annotateOpt(Effect.annotateLogs(effect, "correlationId", cid), "comp", comp), "causationId", causationId), RequestContext$ReventlessCore.tag, RequestContext$ReventlessCore.make(cid, causationId, comp, pluginName, undefined, undefined));
17
+ }
18
+
19
+ function runEffect(correlationId, causationId, comp, pluginName, effect) {
20
+ return Effect.runPromise(annotateInvocation(effect, correlationId, causationId, comp, pluginName));
21
+ }
22
+
23
+ function runEffectHandler(extractCorrelationId, extractCausationId, comp, pluginName, handler) {
24
+ return (event, ctx) => {
25
+ let correlationId = extractCorrelationId !== undefined ? extractCorrelationId(event) : undefined;
26
+ let causationId = extractCausationId !== undefined ? extractCausationId(event) : undefined;
27
+ return Effect.runPromise(annotateInvocation(handler(event, ctx), correlationId, causationId, comp, pluginName));
28
+ };
8
29
  }
9
30
 
10
31
  let CommandHandlerDefaults = {
@@ -16,6 +37,8 @@ let CommandHandlerDefaults = {
16
37
  };
17
38
 
18
39
  export {
40
+ annotateInvocation,
41
+ runEffect,
19
42
  runEffectHandler,
20
43
  CommandHandlerDefaults,
21
44
  }