@reventlessdev/reventless-core 3.0.0-alpha.201 → 3.0.0-alpha.202

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,14 @@
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.202 (2026-08-02)
7
+
8
+ ### Features
9
+
10
+ * **core:** deploy-time offload hook seam for plugin payloads ([7f970eb](https://github.com/ReventlessDev/reventless-core/commit/7f970eb449765e781559c6596ee49dd74b9451bc))
11
+ * **spec:** make plugin structure/apiSchemaFragment offloadable payloads ([a0391bf](https://github.com/ReventlessDev/reventless-core/commit/a0391bf26f91035460cbf90f52d105a1003414c3))
12
+
13
+
6
14
  # 3.0.0-alpha.201 (2026-08-02)
7
15
 
8
16
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.201",
3
+ "version": "3.0.0-alpha.202",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -28,17 +28,17 @@
28
28
  "dependencies": {
29
29
  "sury": "11.0.0-alpha.4",
30
30
  "uuid": "^13.0.0",
31
- "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.0",
32
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
33
32
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
33
+ "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.0",
34
34
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
35
35
  "@reventlessdev/rescript-node": "2.0.0-alpha.0",
36
36
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
37
- "@reventlessdev/rescript-ssh2": "2.0.0-alpha.0",
38
- "@reventlessdev/reventless-infra": "3.0.0-alpha.118",
39
37
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
40
- "@reventlessdev/reventless-interop": "3.0.0-alpha.30",
41
- "@reventlessdev/reventless-spec": "3.0.0-alpha.93"
38
+ "@reventlessdev/rescript-ssh2": "2.0.0-alpha.0",
39
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.119",
40
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.94",
41
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.30"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.0",
@@ -738,6 +738,31 @@ module Make = (
738
738
  // so reading it inside the async callback would always yield "Domain".
739
739
  let capturedDeployTarget = Spec.hooks.deployTarget.contents
740
740
 
741
+ // Offload the two large pluginDefinition fields at deploy time when a
742
+ // provider offers an object store (AWS writes a content-addressed object
743
+ // and returns a reference); unset locally, so they stay Inline. Both
744
+ // values are concrete here, so the hook — and the object write it does —
745
+ // runs at graph-construction time, before the Pulumi.Output.apply below
746
+ // (a resource cannot be created inside .apply). Unconditional: these
747
+ // fields are always large enough to be worth offloading.
748
+ let toPayload = (value, ~schema, ~store) =>
749
+ switch Plugin_Helpers.offloadHook.contents {
750
+ | Some(hook) =>
751
+ Reventless.Offload.Offloaded(
752
+ hook(~store, ~bytes=value->S.reverseConvertToJsonStringOrThrow(schema)),
753
+ )
754
+ | None => Reventless.Offload.Inline(value)
755
+ }
756
+ let apiSchemaFragmentPayload = toPayload(
757
+ apiSchemaFragment,
758
+ ~schema=Reventless.Plugin.apiSchemaFragmentSchema,
759
+ ~store="pluginApiFragments",
760
+ )
761
+ let structurePayload =
762
+ pluginStructure->Option.map(s =>
763
+ toPayload(s, ~schema=Reventless.Plugin.pluginStructureSchema, ~store="pluginStructures")
764
+ )
765
+
741
766
  // DCB EventLog definition — Some when this plugin has a DcbEventLog
742
767
  // component, None otherwise. Admin's manageSubscriptions reads this to
743
768
  // provision cross-plugin SNS subscriptions from peer plugins' DCB
@@ -769,9 +794,9 @@ module Make = (
769
794
  extensions: extensionsDefinitions,
770
795
  eventCollector: eventCollectorUrn,
771
796
  extensionProtocols: [],
772
- apiSchemaFragment: Some(apiSchemaFragment),
797
+ apiSchemaFragment: Some(apiSchemaFragmentPayload),
773
798
  apiTarget: Some(capturedDeployTarget),
774
- structure: pluginStructure,
799
+ structure: structurePayload,
775
800
  dcbEventLog: dcbEventLogDef,
776
801
  // Business role from the deploy-time metadata registry (set via
777
802
  // registerPluginMetadata). Unset → Domain. Rides the handshake to the
@@ -8,6 +8,7 @@ import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
8
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
9
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
10
10
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
11
+ import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
11
12
  import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
12
13
  import * as Plugin$ReventlessCore from "./Plugin.res.mjs";
13
14
  import * as StackReference$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/StackReference.res.mjs";
@@ -486,6 +487,22 @@ function Make(Spec) {
486
487
  let match$3 = EventCollectorHelper.make(childName, eventTopics, opts);
487
488
  let eventCollector = match$3[0];
488
489
  let capturedDeployTarget = Spec.hooks.deployTarget.contents;
490
+ let toPayload = (value, schema, store) => {
491
+ let hook = Plugin_Helpers$ReventlessCore.offloadHook.contents;
492
+ if (hook !== undefined) {
493
+ return {
494
+ TAG: "Offloaded",
495
+ _0: hook(store, S.reverseConvertToJsonStringOrThrow(value, schema, undefined))
496
+ };
497
+ } else {
498
+ return {
499
+ TAG: "Inline",
500
+ _0: value
501
+ };
502
+ }
503
+ };
504
+ let apiSchemaFragmentPayload = toPayload(apiSchemaFragment, Plugin$Reventless.apiSchemaFragmentSchema, "pluginApiFragments");
505
+ let structurePayload = Stdlib_Option.map(pluginStructure, s => toPayload(s, Plugin$Reventless.pluginStructureSchema, "pluginStructures"));
489
506
  let dcbOutputs$1 = dcbResult.dcbEventLogOutputs;
490
507
  let dcbEventLogDef;
491
508
  if (dcbOutputs$1 !== undefined) {
@@ -509,9 +526,9 @@ function Make(Spec) {
509
526
  extensions: extensionsDefinitions,
510
527
  eventCollector: param[1],
511
528
  extensionProtocols: [],
512
- apiSchemaFragment: apiSchemaFragment,
529
+ apiSchemaFragment: apiSchemaFragmentPayload,
513
530
  apiTarget: capturedDeployTarget,
514
- structure: pluginStructure,
531
+ structure: structurePayload,
515
532
  dcbEventLog: param[2],
516
533
  kind: Stdlib_Option.getOr(Stdlib_Option.flatMap(Plugin_Helpers$ReventlessCore.pluginMetadataRegistry.contents, m => m.kind), "Domain")
517
534
  }));
@@ -785,6 +785,27 @@ let clearOnPluginDeployed = () => {
785
785
  onPluginDeployedHook.contents = None
786
786
  }
787
787
 
788
+ // ---------------------------------------------------------------------------
789
+ // Deploy-time offload hook — content-addresses a large pluginDefinition field
790
+ // to an object store and returns the reference. Set by a provider that can
791
+ // write objects (AWS creates a BucketObject); unset on the local platform, so
792
+ // the fields stay Inline. `bytes` is the already-serialized JSON of the value;
793
+ // the provider hashes it, stores it at `sha256/<hash>`, and returns the ref.
794
+ // Fired synchronously at graph-construction time (the value is concrete and a
795
+ // resource cannot be created inside a Pulumi.Output.apply).
796
+ // ---------------------------------------------------------------------------
797
+ let offloadHook: ref<option<(~store: string, ~bytes: string) => Reventless.Offload.offloadedRef>> = ref(
798
+ None,
799
+ )
800
+
801
+ let registerOffload = (hook: (~store: string, ~bytes: string) => Reventless.Offload.offloadedRef) => {
802
+ offloadHook.contents = Some(hook)
803
+ }
804
+
805
+ let clearOffload = () => {
806
+ offloadHook.contents = None
807
+ }
808
+
788
809
  // ---------------------------------------------------------------------------
789
810
  // Platform-deployed hook — fires after deployPlatform / makePlatform
790
811
  // completes, providing platform-level metadata with resolved values.
@@ -418,6 +418,18 @@ function clearOnPluginDeployed() {
418
418
  onPluginDeployedHook.contents = undefined;
419
419
  }
420
420
 
421
+ let offloadHook = {
422
+ contents: undefined
423
+ };
424
+
425
+ function registerOffload(hook) {
426
+ offloadHook.contents = hook;
427
+ }
428
+
429
+ function clearOffload() {
430
+ offloadHook.contents = undefined;
431
+ }
432
+
421
433
  let onPlatformDeployedHook = {
422
434
  contents: undefined
423
435
  };
@@ -1015,6 +1027,9 @@ export {
1015
1027
  onPluginDeployedHook,
1016
1028
  registerOnPluginDeployed,
1017
1029
  clearOnPluginDeployed,
1030
+ offloadHook,
1031
+ registerOffload,
1032
+ clearOffload,
1018
1033
  onPlatformDeployedHook,
1019
1034
  lastPlatformDeployedInfo,
1020
1035
  registerOnPlatformDeployed,
@@ -24,13 +24,15 @@ type state = {
24
24
  extensions: array<Reventless.Plugin.extensionDefinition>,
25
25
  status: status,
26
26
  statusChange: Message.statusChange,
27
- apiSchemaFragment: @s.matches(Reventless.Plugin.apiSchemaFragmentOptionSchema) option<Reventless.Plugin.apiSchemaFragment>,
27
+ apiSchemaFragment: @s.matches(Reventless.Plugin.apiSchemaFragmentOffloadSchema)
28
+ option<Reventless.Offload.payload<Reventless.Plugin.apiSchemaFragment>>,
28
29
  // API target for split-API schema routing. Absent/None means "Domain" (backward compat).
29
30
  // "Platform" → excluded from DomainApi runtime schema stitching in updateApiSchema.
30
31
  apiTarget?: string,
31
32
  // Plugin structure (component metadata) — surfaced via Platform_ComponentDefinitions.
32
33
  // None for older plugins whose protocol version did not carry the field.
33
- structure: @s.matches(Reventless.Plugin.pluginStructureOptionSchema) option<Reventless.Plugin.pluginStructure>,
34
+ structure: @s.matches(Reventless.Plugin.pluginStructureOffloadSchema)
35
+ option<Reventless.Offload.payload<Reventless.Plugin.pluginStructure>>,
34
36
  // DCB EventLog definition for plugins that bundle a DcbEventLog component.
35
37
  // Admin's manageSubscriptions uses this to wire cross-plugin SNS subscriptions
36
38
  // from this plugin's DCB topic → peer EventCollectors (and vice-versa). None
@@ -72,9 +74,9 @@ type queryResult = {
72
74
  extensionNames: array<string>,
73
75
  extensions: array<Reventless.Plugin.extensionDefinition>,
74
76
  status: status,
75
- apiSchemaFragment: option<Reventless.Plugin.apiSchemaFragment>,
77
+ apiSchemaFragment: option<Reventless.Offload.payload<Reventless.Plugin.apiSchemaFragment>>,
76
78
  apiTarget?: string,
77
- structure: option<Reventless.Plugin.pluginStructure>,
79
+ structure: option<Reventless.Offload.payload<Reventless.Plugin.pluginStructure>>,
78
80
  dcbEventLog: option<Reventless.Plugin.dcbEventLogDefinition>,
79
81
  kind: option<Reventless.Plugin.pluginKind>,
80
82
  }
@@ -23,9 +23,9 @@ let stateSchema = S.schema(s => ({
23
23
  extensions: s.m(S.array(Plugin$Reventless.extensionDefinitionSchema)),
24
24
  status: s.m(statusSchema),
25
25
  statusChange: s.m(Message$ReventlessCore.statusChangeSchema),
26
- apiSchemaFragment: s.m(Plugin$Reventless.apiSchemaFragmentOptionSchema),
26
+ apiSchemaFragment: s.m(Plugin$Reventless.apiSchemaFragmentOffloadSchema),
27
27
  apiTarget: s.m(S.option(S.string)),
28
- structure: s.m(Plugin$Reventless.pluginStructureOptionSchema),
28
+ structure: s.m(Plugin$Reventless.pluginStructureOffloadSchema),
29
29
  dcbEventLog: s.m(Plugin$Reventless.dcbEventLogOptionSchema),
30
30
  kind: s.m(S.option(Plugin$Reventless.pluginKindSchema)),
31
31
  otherConnectedVersions: s.m(S.array(Plugin$Reventless.versionSchema))
@@ -109,7 +109,7 @@ describe("Message should", () => {
109
109
  extensionProtocols: [],
110
110
  apiSchemaFragment: None,
111
111
  apiTarget: None,
112
- structure: Some({
112
+ structure: Some(Reventless.Offload.Inline({
113
113
  readModels: [],
114
114
  stateViewSlices: [],
115
115
  stateChangeSlices: [],
@@ -132,7 +132,7 @@ describe("Message should", () => {
132
132
  extensionPoints: None,
133
133
  requiredStores: None,
134
134
  requiredStoreDeclarations: None,
135
- }),
135
+ })),
136
136
  dcbEventLog: None,
137
137
  kind: Domain,
138
138
  }
@@ -186,7 +186,8 @@ describe("Message should", () => {
186
186
  },
187
187
  )->toBe("Domain")
188
188
  expect(d.apiSchemaFragment->Option.isNone)->toBe(true)
189
- let structure: Reventless.Plugin.pluginStructure = d.structure->Option.getOrThrow
189
+ let structure: Reventless.Plugin.pluginStructure =
190
+ d.structure->Option.flatMap(Reventless.Offload.getInline)->Option.getOrThrow
190
191
  let agg = structure.aggregates->Array.getUnsafe(0)
191
192
  expect(agg.events->Array.length)->toBe(0)
192
193
  expect(agg.chapter->Option.isNone)->toBe(true)
@@ -2,6 +2,7 @@
2
2
 
3
3
  import * as S from "sury/src/S.res.mjs";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Offload$Reventless from "@reventlessdev/reventless-spec/src/semantic/Offload.res.mjs";
5
6
  import * as Message$ReventlessCore from "../../src/Message.res.mjs";
6
7
  import * as PluginSpec$ReventlessCore from "../../src/plugin/lifecycle/PluginSpec.res.mjs";
7
8
 
@@ -100,26 +101,29 @@ globalThis.describe("Message should", () => {
100
101
  apiSchemaFragment: undefined,
101
102
  apiTarget: undefined,
102
103
  structure: {
103
- readModels: [],
104
- stateViewSlices: [],
105
- stateChangeSlices: [],
106
- aggregates: [{
107
- name: "Order",
108
- commands: [],
109
- producedEventTypes: [],
110
- consumedEventTypes: [],
111
- linkedViews: [],
112
- consistencyRead: undefined,
113
- events: [],
114
- chapter: undefined
115
- }],
116
- automationSlices: [],
117
- outboundTranslationSlices: [],
118
- inboundTranslationSlices: [],
119
- extensions: [],
120
- extensionPoints: undefined,
121
- requiredStores: undefined,
122
- requiredStoreDeclarations: undefined
104
+ TAG: "Inline",
105
+ _0: {
106
+ readModels: [],
107
+ stateViewSlices: [],
108
+ stateChangeSlices: [],
109
+ aggregates: [{
110
+ name: "Order",
111
+ commands: [],
112
+ producedEventTypes: [],
113
+ consumedEventTypes: [],
114
+ linkedViews: [],
115
+ consistencyRead: undefined,
116
+ events: [],
117
+ chapter: undefined
118
+ }],
119
+ automationSlices: [],
120
+ outboundTranslationSlices: [],
121
+ inboundTranslationSlices: [],
122
+ extensions: [],
123
+ extensionPoints: undefined,
124
+ requiredStores: undefined,
125
+ requiredStoreDeclarations: undefined
126
+ }
123
127
  },
124
128
  dcbEventLog: undefined,
125
129
  kind: "Domain"
@@ -169,7 +173,7 @@ globalThis.describe("Message should", () => {
169
173
  tmp = match === "Domain" ? "Domain" : "other";
170
174
  globalThis.expect(tmp).toBe("Domain");
171
175
  globalThis.expect(Stdlib_Option.isNone(d$1.apiSchemaFragment)).toBe(true);
172
- let structure = Stdlib_Option.getOrThrow(d$1.structure, undefined);
176
+ let structure = Stdlib_Option.getOrThrow(Stdlib_Option.flatMap(d$1.structure, Offload$Reventless.getInline), undefined);
173
177
  let agg = structure.aggregates[0];
174
178
  globalThis.expect(agg.events.length).toBe(0);
175
179
  globalThis.expect(Stdlib_Option.isNone(agg.chapter)).toBe(true);