@reventlessdev/reventless-aws 3.0.0-alpha.269 → 3.0.0-alpha.271

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 (65) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +9 -9
  3. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +1 -0
  4. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +1 -1
  5. package/src/adapter/Api/Platform_UIFragments_Lambda.res +1 -0
  6. package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +1 -1
  7. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +1 -1
  8. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +1 -0
  9. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +1 -1
  10. package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +1 -0
  11. package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res.mjs +1 -1
  12. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
  13. package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
  14. package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +1 -1
  15. package/src/adapter/Runtime/AggregateEntryPoint.mjs +6 -0
  16. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +3 -3
  17. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res.mjs +3 -3
  18. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res.mjs +1 -1
  19. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res.mjs +1 -1
  20. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +1 -1
  21. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +1 -1
  22. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +6 -0
  23. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +2 -2
  24. package/src/adapter/Runtime/CounterEntryPoint.mjs +6 -1
  25. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +6 -0
  26. package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +6 -1
  27. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +1 -1
  28. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +1 -1
  29. package/src/adapter/Runtime/EventMapperEntryPoint.mjs +6 -1
  30. package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +6 -1
  31. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +1 -1
  32. package/src/adapter/Runtime/HandlerFactoryHelpers.mjs +47 -0
  33. package/src/adapter/Runtime/HeartbeatEntryPoint.res +9 -0
  34. package/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs +5 -0
  35. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +9 -0
  36. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +5 -1
  37. package/src/adapter/Runtime/PgMigrationEntryPoint.res +9 -0
  38. package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +5 -1
  39. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +6 -1
  40. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +9 -0
  41. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res.mjs +4 -0
  42. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +6 -1
  43. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +28 -0
  44. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +32 -2
  45. package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +123 -0
  46. package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res.mjs +75 -0
  47. package/src/adapter/Runtime/SideEffectEntryPoint.mjs +6 -0
  48. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +1 -1
  49. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +1 -1
  50. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +6 -0
  51. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +1 -1
  52. package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +6 -0
  53. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res.mjs +1 -1
  54. package/src/adapter/StateTopic/StateTopic_AppSync.res +1 -0
  55. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +1 -1
  56. package/src/adapter/Upload/Upload_Claim_S3.res +1 -0
  57. package/src/adapter/Upload/Upload_Claim_S3.res.mjs +1 -1
  58. package/src/adapter/Upload/Upload_Presign_S3.res +1 -0
  59. package/src/adapter/Upload/Upload_Presign_S3.res.mjs +1 -1
  60. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -1
  61. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +2 -2
  62. package/src/util/Util_Bundle.res +40 -0
  63. package/src/util/Util_Bundle.res.mjs +19 -1
  64. package/tests/RuntimeExtensionEntryPoint_OpsTest.res +110 -0
  65. package/tests/RuntimeExtensionEntryPoint_OpsTest.res.mjs +88 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
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.271 (2026-08-08)
7
+
8
+ **Note:** Version bump only for package @reventlessdev/reventless-aws
9
+
10
+
11
+
12
+
13
+
14
+ # 3.0.0-alpha.270 (2026-08-08)
15
+
16
+ ### Features
17
+
18
+ * **core,aws,local:** add the RuntimeExtension cold-start seam ([143e30e](https://github.com/ReventlessDev/reventless-core/commit/143e30eeded6232ce7f0fcc18328939b2917bb31))
19
+
20
+
6
21
  # 3.0.0-alpha.269 (2026-08-07)
7
22
 
8
23
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.269",
3
+ "version": "3.0.0-alpha.271",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -12,17 +12,17 @@
12
12
  "sury": "11.0.0-alpha.4",
13
13
  "uuid": "^13.0.0",
14
14
  "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.4",
15
- "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
16
15
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
17
- "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.66",
18
16
  "@reventlessdev/rescript-node": "2.0.0-alpha.2",
19
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
20
- "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
21
- "@reventlessdev/reventless-core": "3.0.0-alpha.214",
17
+ "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.66",
18
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
19
+ "@reventlessdev/reventless-core": "3.0.0-alpha.216",
22
20
  "@reventlessdev/reventless-interop": "3.0.0-alpha.30",
23
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.78",
24
- "@reventlessdev/reventless-spec": "3.0.0-alpha.101",
25
- "@reventlessdev/reventless-infra": "3.0.0-alpha.127"
21
+ "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
22
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.80",
23
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.128",
24
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
25
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.102"
26
26
  },
27
27
  "devDependencies": {
28
28
  "rescript": "12.3.0",
@@ -123,6 +123,7 @@ let make = (
123
123
  let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
124
124
  ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs",
125
125
  ~packageDirs,
126
+ ~bundleRuntimeExtensions=false,
126
127
  )
127
128
 
128
129
  let layers =
@@ -75,7 +75,7 @@ function make(api, pluginReadModelTableName, offloadBucketName, schemaReady, opt
75
75
  "@reventlessdev/reventless-aws",
76
76
  Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
77
77
  ]]);
78
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs", packageDirs, undefined);
78
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs", packageDirs, undefined, false);
79
79
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
80
80
  let lambda = new (Aws.lambda.Function)(name + "Lambda", {
81
81
  handler: "index.handler",
@@ -84,6 +84,7 @@ let make = (
84
84
  let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
85
85
  ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs",
86
86
  ~packageDirs,
87
+ ~bundleRuntimeExtensions=false,
87
88
  )
88
89
 
89
90
  let layers =
@@ -52,7 +52,7 @@ function make(api, uiFragmentRegistryTableName, schemaReady, opts) {
52
52
  "@reventlessdev/reventless-aws",
53
53
  Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
54
54
  ]]);
55
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs", packageDirs, undefined);
55
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs", packageDirs, undefined, false);
56
56
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
57
57
  let lambda = new (Aws.lambda.Function)(name + "Lambda", {
58
58
  handler: "index.handler",
@@ -34,7 +34,7 @@ function make(name, referencesName, referencesDb, countsName, countsDb, param, s
34
34
  let mappingsPkg = Util_Bundle$ReventlessAws.extractPackageName(mappingsModulePath);
35
35
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, specPkg);
36
36
  packageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, mappingsPkg);
37
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/CounterEntryPoint.mjs", packageDirs, undefined);
37
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/CounterEntryPoint.mjs", packageDirs, undefined, undefined);
38
38
  let componentOpts_parent = opts.parent;
39
39
  let componentOpts = {
40
40
  parent: componentOpts_parent
@@ -170,6 +170,7 @@ let make = (
170
170
  let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
171
171
  ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs",
172
172
  ~packageDirs,
173
+ ~bundleRuntimeExtensions=false,
173
174
  )
174
175
 
175
176
  let layers =
@@ -89,7 +89,7 @@ function make(name, topicName, eventTopicOutputs, eventsApi, opts) {
89
89
  "@reventlessdev/reventless-aws",
90
90
  Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
91
91
  ]]);
92
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs", packageDirs, undefined);
92
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs", packageDirs, undefined, false);
93
93
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
94
94
  let appsyncEndpoint = AppSync_EventsApi$ReventlessAws.httpEndpoint(eventsApi);
95
95
  let lambda = new (Aws.lambda.Function)(name + "EventLogSubscriber", {
@@ -109,6 +109,7 @@ let make = (
109
109
  let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
110
110
  ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver_Ops.res.mjs",
111
111
  ~packageDirs,
112
+ ~bundleRuntimeExtensions=false,
112
113
  )
113
114
 
114
115
  let layers =
@@ -56,7 +56,7 @@ function make(api, placeIndexName, nameOpt, opts) {
56
56
  "@reventlessdev/reventless-aws",
57
57
  Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
58
58
  ]]);
59
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver_Ops.res.mjs", packageDirs, undefined);
59
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver_Ops.res.mjs", packageDirs, undefined, false);
60
60
  let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
61
61
  let lambda = new (Aws.lambda.Function)(name + "Lambda", {
62
62
  handler: "index.handler",
@@ -23,7 +23,7 @@ import * as RuntimeEnvironment_Lambda$ReventlessAws from "../Runtime/RuntimeEnvi
23
23
  function make(name, logs, securityGroupId, subnetIds, intervalMinutesOpt, opts) {
24
24
  let intervalMinutes = intervalMinutesOpt !== undefined ? intervalMinutesOpt : 1;
25
25
  let customOpts = Stdlib_Option.map(opts, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions);
26
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs", {}, undefined);
26
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs", {}, undefined, undefined);
27
27
  let logJsonOutputs = logs.map(l => Pulumi.all([
28
28
  l.connectionConfig,
29
29
  l.targetQueueUrl
@@ -11,7 +11,7 @@ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
11
11
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../Runtime/RuntimeEnvironment_Lambda.res.mjs";
12
12
 
13
13
  function make(name, handlerConfig, secretArn, securityGroupId, subnetIds, opts) {
14
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs", {}, undefined);
14
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs", {}, undefined, undefined);
15
15
  let sourceCodeHash = match.sourceCodeHash;
16
16
  let envVars = Object.fromEntries([[
17
17
  "HANDLER_CONFIG",
@@ -84,7 +84,7 @@ function provision(api, selection, opts) {
84
84
  let envVars = {};
85
85
  envVars["QUERY_RESOLVER_CONFIG"] = queryResolverConfig;
86
86
  envVars["Environment"] = Pulumi.getStack();
87
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs", packageDirs, undefined);
87
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs", packageDirs, undefined, undefined);
88
88
  let vpcConfig = selection.securityGroupId.apply(sgId => ({
89
89
  subnetIds: selection.subnetIds,
90
90
  securityGroupIds: [sgId]
@@ -12,6 +12,7 @@ import {
12
12
  extractMetaField,
13
13
  extractSentTimestamp,
14
14
  extractRetryCount,
15
+ runtimeExtensionsReady,
15
16
  } from "./HandlerFactoryHelpers.mjs";
16
17
  import { Make as eventLogOperationsMake } from "@reventlessdev/reventless-core/src/components/EventLog/EventLog_Operations.res.mjs";
17
18
  import { Make as aggregateCallbackMake } from "@reventlessdev/reventless-core/src/components/Aggregate/Aggregate_Callback.res.mjs";
@@ -256,6 +257,11 @@ export async function buildHandlersForConfig(config, opts = {}) {
256
257
  }
257
258
 
258
259
  async function buildAllHandlers() {
260
+ // Runtime extension seam: any registered out-of-tree extension gets its
261
+ // onColdStart before a single handler is built, which is what makes the
262
+ // framework's interception/publish hooks reachable here. No-op unless the
263
+ // deployment registered one.
264
+ await runtimeExtensionsReady;
259
265
  const configStr = process.env["HANDLER_CONFIG"] || '{"handlers":[]}';
260
266
  return buildHandlersForConfig(JSON.parse(configStr));
261
267
  }
@@ -173,7 +173,7 @@ function finish() {
173
173
  ]).apply(param => `{"handlers":[{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment + `}]}`);
174
174
  let cmdTopicEnvVars = {};
175
175
  cmdTopicEnvVars["HANDLER_CONFIG"] = cmdTopicHandlerConfigOutput;
176
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
176
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
177
177
  let cmdTopicName = baseName + "CmdHandler";
178
178
  let cmdTopicRuntime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(cmdTopicName, "CommandHandler", "Aggregate", match.code, match.sourceCodeHash, cmdTopicEnvVars, Math.max(spec.commandTopicMemorySize, 1024), Math.max(spec.commandTopicTimeout, 30), undefined, undefined, undefined, undefined, undefined, aggregateOpts);
179
179
  spec.commandTopicConnects.forEach(connect => connect(cmdTopicRuntime));
@@ -186,7 +186,7 @@ function finish() {
186
186
  ]).apply(param => `{"handlers":[{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment$1 + `}]}`);
187
187
  let cmdGenEnvVars = {};
188
188
  cmdGenEnvVars["HANDLER_CONFIG"] = cmdGenHandlerConfigOutput;
189
- let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
189
+ let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
190
190
  let cmdGenName = baseName + "CmdGen";
191
191
  let cmdGenRuntime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(cmdGenName, "CommandHandler", "Aggregate", match$1.code, match$1.sourceCodeHash, cmdGenEnvVars, Math.max(spec.commandGeneratorMemorySize, 1024), Math.max(spec.commandGeneratorTimeout, 30), undefined, undefined, undefined, undefined, undefined, aggregateOpts);
192
192
  spec.commandGeneratorConnects.forEach(connect => connect(cmdGenRuntime));
@@ -209,7 +209,7 @@ function finish() {
209
209
  let mappingsPkg = Util_Bundle$ReventlessAws.extractPackageName(match$3);
210
210
  evtMapperPackageDirs[specPkg$1] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, specPkg$1);
211
211
  evtMapperPackageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, mappingsPkg);
212
- let match$4 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/EventMapperEntryPoint.mjs", evtMapperPackageDirs, undefined);
212
+ let match$4 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/EventMapperEntryPoint.mjs", evtMapperPackageDirs, undefined, undefined);
213
213
  let evtMapperName = baseName + "EventMapper";
214
214
  let evtMapperRuntime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(evtMapperName, "CommandHandler", "Aggregate", match$4.code, match$4.sourceCodeHash, evtMapperEnvVars, Math.max(spec.eventCollectorMemorySize, 2048), Math.max(spec.eventCollectorTimeout, 180), undefined, undefined, undefined, undefined, undefined, aggregateOpts);
215
215
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect(evtMapperName, [match$2], evtMapperRuntime, aggregateOpts);
@@ -173,7 +173,7 @@ function finish() {
173
173
  ]).apply(param => `{"handlers":[{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment + `}]}`);
174
174
  let cmdTopicEnvVars = {};
175
175
  cmdTopicEnvVars["HANDLER_CONFIG"] = cmdTopicHandlerConfigOutput;
176
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
176
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
177
177
  let cmdTopicName = baseName + "CmdHandler";
178
178
  let cmdTopicRuntime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(cmdTopicName, "CommandHandler", "Aggregate", match.code, match.sourceCodeHash, cmdTopicEnvVars, Math.max(spec.commandTopicMemorySize, 1024), Math.max(spec.commandTopicTimeout, 30), undefined, undefined, undefined, undefined, undefined, aggregateOpts);
179
179
  spec.commandTopicConnects.forEach(connect => connect(cmdTopicRuntime));
@@ -187,7 +187,7 @@ function finish() {
187
187
  let cmdGenEnvVars = {};
188
188
  cmdGenEnvVars["HANDLER_CONFIG"] = cmdGenHandlerConfigOutput;
189
189
  cmdGenEnvVars["DISPATCH_MODE"] = "async";
190
- let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
190
+ let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
191
191
  let cmdGenName = baseName + "CmdGen";
192
192
  let cmdGenRuntime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(cmdGenName, "CommandHandler", "Aggregate", match$1.code, match$1.sourceCodeHash, cmdGenEnvVars, Math.max(spec.commandGeneratorMemorySize, 1024), Math.max(spec.commandGeneratorTimeout, 30), undefined, undefined, undefined, undefined, undefined, aggregateOpts);
193
193
  spec.commandGeneratorConnects.forEach(connect => connect(cmdGenRuntime));
@@ -210,7 +210,7 @@ function finish() {
210
210
  let mappingsPkg = Util_Bundle$ReventlessAws.extractPackageName(match$3);
211
211
  evtMapperPackageDirs[specPkg$1] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, specPkg$1);
212
212
  evtMapperPackageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, mappingsPkg);
213
- let match$4 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/EventMapperEntryPoint.mjs", evtMapperPackageDirs, undefined);
213
+ let match$4 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/EventMapperEntryPoint.mjs", evtMapperPackageDirs, undefined, undefined);
214
214
  let evtMapperName = baseName + "EventMapper";
215
215
  let evtMapperRuntime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(evtMapperName, "CommandHandler", "Aggregate", match$4.code, match$4.sourceCodeHash, evtMapperEnvVars, Math.max(spec.eventCollectorMemorySize, 2048), Math.max(spec.eventCollectorTimeout, 180), undefined, undefined, undefined, undefined, undefined, aggregateOpts);
216
216
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect(evtMapperName, [match$2], evtMapperRuntime, aggregateOpts);
@@ -153,7 +153,7 @@ function finish() {
153
153
  let behaviorPkg = Util_Bundle$ReventlessAws.extractPackageName(info.behaviorModulePath);
154
154
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, specPkg);
155
155
  packageDirs[behaviorPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, behaviorPkg);
156
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
156
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
157
157
  let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(lambdaName, "CommandHandler", "Aggregate", match.code, match.sourceCodeHash, envVars, spec.memorySize, spec.timeout, undefined, undefined, undefined, undefined, undefined, aggregateOpts);
158
158
  spec.connects.forEach(connect => connect(runtime));
159
159
  let channelSpecs = Stdlib_Option.mapOr(spec.eventCollectorChannelSpec, [], cs => [cs]);
@@ -153,7 +153,7 @@ function finish() {
153
153
  let behaviorPkg = Util_Bundle$ReventlessAws.extractPackageName(info.behaviorModulePath);
154
154
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, specPkg);
155
155
  packageDirs[behaviorPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, behaviorPkg);
156
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
156
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
157
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);
158
158
  spec.connects.forEach(connect => connect(runtime));
159
159
  let channelSpecs = Stdlib_Option.mapOr(spec.eventCollectorChannelSpec, [], cs => [cs]);
@@ -200,7 +200,7 @@ function finish() {
200
200
  Stdlib_Option.forEach(cfg.logLevel, level => {
201
201
  envVars["LOG_LEVEL"] = Pulumi.output(level);
202
202
  });
203
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
203
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
204
204
  Stdlib_Option.forEach(cfg.sqsBatchSize, CommandTopicChannel_SQS_Sync$ReventlessAws.setBatchSize);
205
205
  let vpcConfig = pgSelection !== undefined ? pgSelection.securityGroupId.apply(sgId => ({
206
206
  subnetIds: pgSelection.subnetIds,
@@ -189,7 +189,7 @@ function finish() {
189
189
  Stdlib_Option.forEach(cfg.logLevel, level => {
190
190
  envVars["LOG_LEVEL"] = Pulumi.output(level);
191
191
  });
192
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
192
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined, undefined);
193
193
  Stdlib_Option.forEach(cfg.sqsBatchSize, CommandTopicChannel_SQS_Async$ReventlessAws.setBatchSize);
194
194
  let vpcConfig = pgSelection !== undefined ? pgSelection.securityGroupId.apply(sgId => ({
195
195
  subnetIds: pgSelection.subnetIds,
@@ -17,10 +17,16 @@ import { Make as automationSliceCallbackMake } from "@reventlessdev/reventless-c
17
17
  import { Make as outboundTranslationSliceCallbackMake } from "@reventlessdev/reventless-core/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
18
18
  import * as StreamOps from "./StreamRoutedEntryPoint_Ops.res.mjs";
19
19
  import * as Ops from "./AutomationSliceEntryPoint_Ops.res.mjs";
20
+ import { runtimeExtensionsReady } from "./HandlerFactoryHelpers.mjs";
20
21
 
21
22
  const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
22
23
 
23
24
  async function buildAllHandlers() {
25
+ // Runtime extension seam: any registered out-of-tree extension gets its
26
+ // onColdStart before a single handler is built, which is what makes the
27
+ // framework's interception/publish hooks reachable here. No-op unless the
28
+ // deployment registered one.
29
+ await runtimeExtensionsReady;
24
30
  const handlers = {};
25
31
  const sweeps = [];
26
32
  const entries = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "");
@@ -206,7 +206,7 @@ function finish() {
206
206
  PluginRuntime_Builder$ReventlessAws.capabilityEnv().forEach(param => {
207
207
  envVars[param[0]] = param[1];
208
208
  });
209
- let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined);
209
+ let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined, undefined);
210
210
  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);
211
211
  let channelSpecs = storedSpecs.map(param => param.channelSpec);
212
212
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllAutomationSlices", channelSpecs, runtime, opts);
@@ -318,7 +318,7 @@ function finishWithDcbEventLog(dcbEventLog) {
318
318
  PluginRuntime_Builder$ReventlessAws.capabilityEnv().forEach(param => {
319
319
  envVars[param[0]] = param[1];
320
320
  });
321
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined);
321
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined, undefined);
322
322
  let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAutomationSlices", "Reactor", "AutomationSlice", match.code, match.sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, undefined, opts);
323
323
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllAutomationSlices", [{
324
324
  channel: channel,
@@ -12,13 +12,18 @@
12
12
  // references/counts stream routing live type-checked in
13
13
  // CounterEntryPoint_Ops.res.
14
14
 
15
- import { patchSpecId } from "./HandlerFactoryHelpers.mjs";
15
+ import { patchSpecId, runtimeExtensionsReady } from "./HandlerFactoryHelpers.mjs";
16
16
  import { MakeCounterHandler } from "@reventlessdev/reventless-core/src/components/EventMapper/EventMapper_Callback.res.mjs";
17
17
  import * as Ops from "./CounterEntryPoint_Ops.res.mjs";
18
18
 
19
19
  const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
20
20
 
21
21
  async function build() {
22
+ // Runtime extension seam: any registered out-of-tree extension gets its
23
+ // onColdStart before a single handler is built, which is what makes the
24
+ // framework's interception/publish hooks reachable here. No-op unless the
25
+ // deployment registered one.
26
+ await runtimeExtensionsReady;
22
27
  const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "{}");
23
28
 
24
29
  const targetSpecModule = await dynamicImport(config.targetSpecModule);
@@ -15,6 +15,7 @@ import {
15
15
  extractMetaField,
16
16
  extractSentTimestamp,
17
17
  extractRetryCount,
18
+ runtimeExtensionsReady,
18
19
  } from "./HandlerFactoryHelpers.mjs";
19
20
  import { Make as dcbEventLogOperationsMake } from "@reventlessdev/reventless-core/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs";
20
21
  import { makeCommandGenerator } from "./CommandGeneratorEntryPoint_Ops.res.mjs";
@@ -233,6 +234,11 @@ export async function buildHandlersForConfig(config, opts = {}) {
233
234
  }
234
235
 
235
236
  async function buildHandler() {
237
+ // Runtime extension seam: any registered out-of-tree extension gets its
238
+ // onColdStart before a single handler is built, which is what makes the
239
+ // framework's interception/publish hooks reachable here. No-op unless the
240
+ // deployment registered one.
241
+ await runtimeExtensionsReady;
236
242
  const configStr = process.env["HANDLER_CONFIG"];
237
243
  // Importing this module without HANDLER_CONFIG (e.g. from a test that drives
238
244
  // `buildHandlersForConfig` directly) must not crash module evaluation.
@@ -66,7 +66,7 @@
66
66
 
67
67
  import { createRequire } from "node:module";
68
68
  import { pathToFileURL } from "node:url";
69
- import { patchSpecId } from "./HandlerFactoryHelpers.mjs";
69
+ import { patchSpecId, runtimeExtensionsReady } from "./HandlerFactoryHelpers.mjs";
70
70
  import { Make as extensionPointOperationsMake } from "@reventlessdev/reventless-core/src/components/ExtensionPoint/ExtensionPoint_Operations.res.mjs";
71
71
  import { Make as extensionOperationsMake } from "@reventlessdev/reventless-core/src/components/Extension/Extension_Operations.res.mjs";
72
72
  import { Make as extensionMappingMake } from "@reventlessdev/reventless-infra/src/types/ExtensionMapping.res.mjs";
@@ -93,6 +93,11 @@ const importFromAsset = (() => {
93
93
  })();
94
94
 
95
95
  async function buildHandler() {
96
+ // Runtime extension seam: any registered out-of-tree extension gets its
97
+ // onColdStart before a single handler is built, which is what makes the
98
+ // framework's interception/publish hooks reachable here. No-op unless the
99
+ // deployment registered one.
100
+ await runtimeExtensionsReady;
96
101
  const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "");
97
102
  const pluginDefinition = Ops.loadPluginDefinition();
98
103
 
@@ -62,7 +62,7 @@ function forEventCollector(param, eventTopics, resources, memorySizeOpt, timeout
62
62
  let mappingsPkg = Util_Bundle$ReventlessAws.extractPackageName(info.mappingsModulePath);
63
63
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, specPkg);
64
64
  packageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, mappingsPkg);
65
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ReadModelEntryPoint.mjs", packageDirs, undefined);
65
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ReadModelEntryPoint.mjs", packageDirs, undefined, undefined);
66
66
  let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "EventCollector", "EventCollector", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts);
67
67
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect(name, [{
68
68
  channel: channel,
@@ -156,7 +156,7 @@ function finish() {
156
156
  if (pgStreamConfig !== undefined) {
157
157
  envVars["APPSYNC_ENDPOINT"] = pgStreamConfig.endpoint;
158
158
  }
159
- let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ReadModelEntryPoint.mjs", packageDirs, undefined);
159
+ let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ReadModelEntryPoint.mjs", packageDirs, undefined, undefined);
160
160
  let vpcConfig = qdbSelection !== undefined && anyPgBacked ? qdbSelection.securityGroupId.apply(sgId => ({
161
161
  subnetIds: qdbSelection.subnetIds,
162
162
  securityGroupIds: [sgId]
@@ -13,7 +13,7 @@
13
13
  // EventMapperEntryPoint_Ops.res.
14
14
 
15
15
  import { $$String as IdString } from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
16
- import { patchSpecId } from "./HandlerFactoryHelpers.mjs";
16
+ import { patchSpecId, runtimeExtensionsReady } from "./HandlerFactoryHelpers.mjs";
17
17
  import { MakeCounterHandler } from "@reventlessdev/reventless-core/src/components/EventMapper/EventMapper_Callback.res.mjs";
18
18
  import * as Ops from "./EventMapperEntryPoint_Ops.res.mjs";
19
19
 
@@ -34,6 +34,11 @@ function patchMappingsSourceIds(mappingsModule) {
34
34
  }
35
35
 
36
36
  async function build() {
37
+ // Runtime extension seam: any registered out-of-tree extension gets its
38
+ // onColdStart before a single handler is built, which is what makes the
39
+ // framework's interception/publish hooks reachable here. No-op unless the
40
+ // deployment registered one.
41
+ await runtimeExtensionsReady;
37
42
  const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "{}");
38
43
 
39
44
  const targetSpecModule = await dynamicImport(config.targetSpecModule);
@@ -11,7 +11,7 @@
11
11
  // HANDLER_CONFIG parsing, and the SQS dispatch boundary live type-checked in
12
12
  // ExtensionPointEntryPoint_Ops.res.
13
13
 
14
- import { patchSpecId } from "./HandlerFactoryHelpers.mjs";
14
+ import { patchSpecId, runtimeExtensionsReady } from "./HandlerFactoryHelpers.mjs";
15
15
  import { Make as extensionPointCallbackMake } from "@reventlessdev/reventless-core/src/components/ExtensionPoint/ExtensionPoint_Callback.res.mjs";
16
16
  import { Make as commandTopicCallbackMake } from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Callback.res.mjs";
17
17
  import * as Ops from "./ExtensionPointEntryPoint_Ops.res.mjs";
@@ -19,6 +19,11 @@ import * as Ops from "./ExtensionPointEntryPoint_Ops.res.mjs";
19
19
  const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
20
20
 
21
21
  async function build() {
22
+ // Runtime extension seam: any registered out-of-tree extension gets its
23
+ // onColdStart before a single handler is built, which is what makes the
24
+ // framework's interception/publish hooks reachable here. No-op unless the
25
+ // deployment registered one.
26
+ await runtimeExtensionsReady;
22
27
  const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "{}");
23
28
 
24
29
  const specModule = await dynamicImport(config.specModule);
@@ -38,7 +38,7 @@ function forCommandTopic(param, connect, memorySizeOpt, timeoutOpt, specModuleUr
38
38
  let mappingsPkg = Util_Bundle$ReventlessAws.extractPackageName(mappingsModulePath);
39
39
  packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, specPkg);
40
40
  packageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, mappingsPkg);
41
- let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ExtensionPointEntryPoint.mjs", packageDirs, undefined);
41
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ExtensionPointEntryPoint.mjs", packageDirs, undefined, undefined);
42
42
  return connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "CommandHandler", "ExtensionPoint", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
43
43
  }
44
44
 
@@ -8,6 +8,11 @@ import {
8
8
  } from "@reventlessdev/reventless-core/src/RequestContext.res.mjs";
9
9
  import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
10
10
  import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
11
+ import {
12
+ parseConfig as parseRuntimeExtensionConfig,
13
+ fire as fireRuntimeExtensions,
14
+ reportLoadFailure as reportRuntimeExtensionLoadFailure,
15
+ } from "./RuntimeExtensionEntryPoint_Ops.res.mjs";
11
16
 
12
17
  // ─── Structured logging from the .mjs entry-point shims ─────────────────────
13
18
  // Mirrors ReScript's Logger.t API and Logger.emit's JSON sink shape (time,
@@ -192,6 +197,48 @@ export function extractRetryCount(records) {
192
197
  return Number.isFinite(count) && count > 0 ? count : 1;
193
198
  }
194
199
 
200
+ // ─── Runtime extension cold start ───────────────────────────────────────────
201
+ // Extension packages are bundled into the code archive next to the spec and
202
+ // behavior packages, so they resolve from /var/task/node_modules by the same
203
+ // absolute path every shell uses for user modules.
204
+ const dynamicImport = (specifier) => import("/var/task/node_modules/" + specifier);
205
+
206
+ // The framework ships four runtime callback hooks (command interception, query
207
+ // interception, before/after publish) whose registrars are module-level refs.
208
+ // They are only reachable if something calls them in this process before the
209
+ // first request, and the entry shells import framework and domain modules only —
210
+ // so an out-of-tree extension had no way in. RUNTIME_EXTENSIONS closes that: it
211
+ // names the extension modules the archive carries (see
212
+ // ReventlessCore.RuntimeExtension and Util_Bundle.addRuntimeExtensionPackages),
213
+ // and each one's `onColdStart` runs here, once, before any handler is built.
214
+ //
215
+ // This is the seam's one untyped step: the modules are named at deploy time and
216
+ // their types are unknowable here. Parsing and invocation live in the typed
217
+ // RuntimeExtensionEntryPoint_Ops.res, so the call's arity is compiler-checked.
218
+ //
219
+ // Started at module load and exported as a promise every entry shell awaits in
220
+ // its own cold-start init — that is what makes "before the first request" a fact
221
+ // rather than a hope, and it holds for the shells that build handlers lazily.
222
+ // Absent env var ⇒ resolves immediately, having done nothing.
223
+ export const runtimeExtensionsReady = (async () => {
224
+ const config = parseRuntimeExtensionConfig(process.env["RUNTIME_EXTENSIONS"]);
225
+ if (config === undefined) return;
226
+ const hooks = [];
227
+ for (const specifier of config.modules) {
228
+ try {
229
+ const mod = await dynamicImport(specifier);
230
+ if (typeof mod.onColdStart === "function") {
231
+ hooks.push(mod.onColdStart);
232
+ } else {
233
+ reportRuntimeExtensionLoadFailure(specifier, "module exports no onColdStart function");
234
+ }
235
+ } catch (err) {
236
+ reportRuntimeExtensionLoadFailure(specifier, err?.message ?? String(err));
237
+ }
238
+ }
239
+ fireRuntimeExtensions(config, hooks);
240
+ })();
241
+
195
242
  // Patch a spec module's Id field to use IdString if undefined.
196
243
  // Workaround for `module Id = Id.String` not producing an ESM export.
197
244
  export const patchSpecId = (specModule) => ({ ...specModule, Id: specModule.Id || IdString });
@@ -26,7 +26,16 @@ let publishJsons = queue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS_F
26
26
 
27
27
  // === Exported handler ===
28
28
 
29
+ // Runtime extension seam: HandlerFactoryHelpers loads and fires any registered
30
+ // out-of-tree extension once, at module load. Awaiting its promise before this
31
+ // handler does any work is what makes "before the first request" a fact rather
32
+ // than a hope; it is already resolved on every invocation after the first, and
33
+ // resolves immediately when nothing is registered.
34
+ @module("./HandlerFactoryHelpers.mjs")
35
+ external runtimeExtensionsReady: promise<unit> = "runtimeExtensionsReady"
36
+
29
37
  let handler = async (_event: JSON.t, _context: PulumiAws.Lambda.context) => {
38
+ await runtimeExtensionsReady
30
39
  let message: ReventlessCore.Message.commandJson = {
31
40
  id: pluginId,
32
41
  meta: ReventlessCore.Message.generateMeta(
@@ -4,6 +4,7 @@ import * as S from "sury/src/S.res.mjs";
4
4
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
5
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
6
  import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
7
+ import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
7
8
  import * as PluginExtensionPointSpec$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
8
9
  import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
9
10
 
@@ -21,7 +22,10 @@ let queue = {
21
22
 
22
23
  let publishJsons = CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO");
23
24
 
25
+ let runtimeExtensionsReady = HandlerFactoryHelpersMjs.runtimeExtensionsReady;
26
+
24
27
  async function handler(_event, _context) {
28
+ await runtimeExtensionsReady;
25
29
  let message_meta = Message$ReventlessCore.generateMeta(PluginExtensionPointSpec$ReventlessInfra.name, "", "Heartbeat", undefined, undefined, undefined, undefined, undefined);
26
30
  let message_commandJson = S.reverseConvertToJsonOrThrow({
27
31
  TAG: "Heartbeat",
@@ -42,6 +46,7 @@ export {
42
46
  timeout,
43
47
  queue,
44
48
  publishJsons,
49
+ runtimeExtensionsReady,
45
50
  handler,
46
51
  }
47
52
  /* epQueueUrl Not a pure module */
@@ -86,7 +86,16 @@ let relayLog = async (l: relayLogConfig): unit => {
86
86
  }
87
87
  }
88
88
 
89
+ // Runtime extension seam: HandlerFactoryHelpers loads and fires any registered
90
+ // out-of-tree extension once, at module load. Awaiting its promise before this
91
+ // handler does any work is what makes "before the first request" a fact rather
92
+ // than a hope; it is already resolved on every invocation after the first, and
93
+ // resolves immediately when nothing is registered.
94
+ @module("./HandlerFactoryHelpers.mjs")
95
+ external runtimeExtensionsReady: promise<unit> = "runtimeExtensionsReady"
96
+
89
97
  let handler = async (_event: JSON.t, _context: PulumiAws.Lambda.context) => {
98
+ await runtimeExtensionsReady
90
99
  let config =
91
100
  NodeProcess.env->Dict.get("HANDLER_CONFIG")->Option.getOr(`{"logs":[]}`)->parseHandlerConfig
92
101
  let logs = config.logs->Option.getOr([])