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

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,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.203 (2026-08-02)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **core:** run the offload hook at graph-construction, not inside .apply ([5a102f1](https://github.com/ReventlessDev/reventless-core/commit/5a102f149d6d218cdeeb28dbf9bded1d6fb1252e))
11
+
12
+
13
+ # 3.0.0-alpha.202 (2026-08-02)
14
+
15
+ ### Features
16
+
17
+ * **core:** deploy-time offload hook seam for plugin payloads ([7f970eb](https://github.com/ReventlessDev/reventless-core/commit/7f970eb449765e781559c6596ee49dd74b9451bc))
18
+ * **spec:** make plugin structure/apiSchemaFragment offloadable payloads ([a0391bf](https://github.com/ReventlessDev/reventless-core/commit/a0391bf26f91035460cbf90f52d105a1003414c3))
19
+
20
+
6
21
  # 3.0.0-alpha.201 (2026-08-02)
7
22
 
8
23
  ### 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.203",
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",
32
+ "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.1",
33
33
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
34
34
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
35
- "@reventlessdev/rescript-node": "2.0.0-alpha.0",
35
+ "@reventlessdev/rescript-node": "2.0.0-alpha.1",
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
- "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
37
+ "@reventlessdev/rescript-ssh2": "2.0.0-alpha.1",
38
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.120",
40
39
  "@reventlessdev/reventless-interop": "3.0.0-alpha.30",
41
- "@reventlessdev/reventless-spec": "3.0.0-alpha.93"
40
+ "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
41
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.95"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.0",
@@ -606,6 +606,32 @@ module Make = (
606
606
  | None => Pulumi.Output.make()
607
607
  }
608
608
 
609
+ // Offload the two large pluginDefinition fields at deploy time when a
610
+ // provider offers an object store (AWS writes a content-addressed object
611
+ // and returns a reference); unset locally, so they stay Inline. Computed
612
+ // HERE, at graph-construction time — both values are concrete and the hook
613
+ // (which creates the object resource) MUST run outside the Pulumi.Output.apply
614
+ // below: a resource cannot be created inside .apply, and an apply callback is
615
+ // skipped entirely on `pulumi preview` when its inputs are still unknown.
616
+ // Unconditional: these fields are always large enough to be worth offloading.
617
+ let toPayload = (value, ~schema, ~store) =>
618
+ switch Plugin_Helpers.offloadHook.contents {
619
+ | Some(hook) =>
620
+ Reventless.Offload.Offloaded(
621
+ hook(~store, ~bytes=value->S.reverseConvertToJsonStringOrThrow(schema)),
622
+ )
623
+ | None => Reventless.Offload.Inline(value)
624
+ }
625
+ let apiSchemaFragmentPayload = toPayload(
626
+ apiSchemaFragment,
627
+ ~schema=Reventless.Plugin.apiSchemaFragmentSchema,
628
+ ~store="pluginApiFragments",
629
+ )
630
+ let structurePayload =
631
+ pluginStructure->Option.map(s =>
632
+ toPayload(s, ~schema=Reventless.Plugin.pluginStructureSchema, ~store="pluginStructures")
633
+ )
634
+
609
635
  (
610
636
  (interstackAdminExtensionPoints, localAdminResolvedEP, schemaPushed)->Pulumi.Output.all3,
611
637
  aggregateResources->Pulumi.Output.allDict,
@@ -769,9 +795,9 @@ module Make = (
769
795
  extensions: extensionsDefinitions,
770
796
  eventCollector: eventCollectorUrn,
771
797
  extensionProtocols: [],
772
- apiSchemaFragment: Some(apiSchemaFragment),
798
+ apiSchemaFragment: Some(apiSchemaFragmentPayload),
773
799
  apiTarget: Some(capturedDeployTarget),
774
- structure: pluginStructure,
800
+ structure: structurePayload,
775
801
  dcbEventLog: dcbEventLogDef,
776
802
  // Business role from the deploy-time metadata registry (set via
777
803
  // 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";
@@ -420,6 +421,22 @@ function Make(Spec) {
420
421
  });
421
422
  let pushSchema = Spec.hooks.preResolversSchemaHook;
422
423
  let schemaPushed = pushSchema !== undefined ? pushSchema(extra$1, version, apiSchemaFragment) : Pulumi.output();
424
+ let toPayload = (value, schema, store) => {
425
+ let hook = Plugin_Helpers$ReventlessCore.offloadHook.contents;
426
+ if (hook !== undefined) {
427
+ return {
428
+ TAG: "Offloaded",
429
+ _0: hook(store, S.reverseConvertToJsonStringOrThrow(value, schema, undefined))
430
+ };
431
+ } else {
432
+ return {
433
+ TAG: "Inline",
434
+ _0: value
435
+ };
436
+ }
437
+ };
438
+ let apiSchemaFragmentPayload = toPayload(apiSchemaFragment, Plugin$Reventless.apiSchemaFragmentSchema, "pluginApiFragments");
439
+ let structurePayload = Stdlib_Option.map(pluginStructure, s => toPayload(s, Plugin$Reventless.pluginStructureSchema, "pluginStructures"));
423
440
  let builderOutputs = Pulumi.all([
424
441
  Pulumi.all([
425
442
  interstackAdminExtensionPoints,
@@ -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);