@reventlessdev/reventless-infra 3.0.0-alpha.100 → 3.0.0-alpha.102
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 +16 -0
- package/package.json +9 -9
- package/src/components/Aggregate.res +5 -1
- package/src/components/AutomationSlice.res +1 -0
- package/src/components/ExtensionPoint.res +5 -0
- package/src/components/InboundTranslationSlice.res +1 -0
- package/src/components/OutboundTranslationSlice.res +1 -0
- package/src/components/Plugin.res +6 -0
- package/src/components/ReadModel.res +1 -0
- package/src/components/StateChangeSlice.res +1 -0
- package/src/components/StateViewSlice.res +1 -0
- package/src/components/Task.res +1 -0
- package/src/types/RuntimeDefaults.res +18 -0
- package/src/types/RuntimeDefaults.res.mjs +2 -0
- package/src/types/RuntimeHints.res +41 -0
- package/src/types/RuntimeHints.res.mjs +38 -0
- package/tests/RuntimeHintsTest.res +32 -0
- package/tests/DeployBootstrapTest.res.mjs +0 -100
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.102 (2026-07-17)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **runtime:** platform-supplied EP/task pod runtime floors ([37544d0](https://github.com/ReventlessDev/reventless-core/commit/37544d029d278bbcae6eeb0f583da07d6ce70f04))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.101 (2026-07-16)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **aws:** consume per-component runtime hints; fold into shared command Lambdas ([7ba2485](https://github.com/ReventlessDev/reventless-core/commit/7ba248579f2472f2fc847b26033927199353f3e6))
|
|
18
|
+
* **core:** honor per-component runtime hints for ExtensionPoints ([72d0218](https://github.com/ReventlessDev/reventless-core/commit/72d0218ec4f73d6a59230640c9ee4a538e6b8688))
|
|
19
|
+
* **core:** thread per-component runtime hints from plugin.json (spec/infra/core/local) ([a94f419](https://github.com/ReventlessDev/reventless-core/commit/a94f4199c4ac7eeeb24dd0a71d58fb73c1e514e6))
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
# 3.0.0-alpha.100 (2026-07-15)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @reventlessdev/reventless-infra
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-infra",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.102",
|
|
4
4
|
"description": "Infrastructure types for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
"sury": "11.0.0-alpha.4",
|
|
17
17
|
"sury-ppx": "11.0.0-alpha.2",
|
|
18
18
|
"uuid": "^13.0.0",
|
|
19
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.
|
|
20
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.
|
|
21
|
-
"@reventlessdev/rescript-uuid": "1.1.0-alpha.
|
|
22
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
19
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.29",
|
|
20
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
|
|
21
|
+
"@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
|
|
22
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.79"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"rescript": "
|
|
26
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.
|
|
25
|
+
"rescript": "12.3.0",
|
|
26
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.9"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"rescript": "
|
|
29
|
+
"rescript": "12.3.0"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"registry": "https://registry.npmjs.org"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
|
36
36
|
"url": "git+https://github.com/ReventlessDev/reventless-core.git",
|
|
37
|
-
"directory": "reventless/
|
|
37
|
+
"directory": "reventless/infra"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "rescript build",
|
|
@@ -65,7 +65,11 @@ module type T = {
|
|
|
65
65
|
module Spec: Reventless.Aggregate.Spec
|
|
66
66
|
type api
|
|
67
67
|
type component
|
|
68
|
-
let make: (
|
|
68
|
+
let make: (
|
|
69
|
+
~api: api,
|
|
70
|
+
~runtime: RuntimeHints.t=?,
|
|
71
|
+
~opts: Pulumi.ComponentResource.options=?,
|
|
72
|
+
) => component
|
|
69
73
|
let outputs: component => outputs
|
|
70
74
|
let operations: component => Pulumi.Output.t<operations>
|
|
71
75
|
/** Signal that all event mappers have been attached; finalises the aggregate. */
|
|
@@ -43,6 +43,7 @@ module type T = {
|
|
|
43
43
|
~allEventTopics: EventTopic.allOutputs,
|
|
44
44
|
~publishJsons: Pulumi.Output.t<CommandTopic.publishJsons>,
|
|
45
45
|
~context: Reventless.AutomationSlice.context,
|
|
46
|
+
~runtime: RuntimeHints.t=?,
|
|
46
47
|
~opts: Pulumi.ComponentResource.options=?,
|
|
47
48
|
) => component
|
|
48
49
|
}
|
|
@@ -49,12 +49,17 @@ the `reventless` package.
|
|
|
49
49
|
module type T = {
|
|
50
50
|
type operations
|
|
51
51
|
type component
|
|
52
|
+
/** The extension point's logical name — the key its `plugin.json` runtime
|
|
53
|
+
hint is looked up by at the `Builder_Helpers.createExtensionPoints` site,
|
|
54
|
+
before `make` is called. */
|
|
55
|
+
let name: string
|
|
52
56
|
let make: (
|
|
53
57
|
~aggregateResources: dict<array<Adapter.resource>>,
|
|
54
58
|
~publishToAggregates: dict<CommandTopic.publishJsons>,
|
|
55
59
|
~scheduler: Scheduler.operations,
|
|
56
60
|
~queryEngine: Reventless.QueryEngine.operations,
|
|
57
61
|
~resourceNaming: ResourceNaming.operations,
|
|
62
|
+
~runtime: RuntimeHints.t=?,
|
|
58
63
|
~opts: option<Pulumi.ComponentResource.options>,
|
|
59
64
|
) => component
|
|
60
65
|
let outputs: component => outputs
|
|
@@ -66,6 +66,12 @@ module type T = {
|
|
|
66
66
|
`@@reventless.systemCallable` on the spec file and threaded here by the
|
|
67
67
|
plugin generator; see `docs/guides/appsync-iam-system-caller.md`. */
|
|
68
68
|
~systemCallableComponents: array<string>=?,
|
|
69
|
+
/** Per-component runtime resource hints keyed by component name, authored in
|
|
70
|
+
the plugin's `plugin.json` `runtime` block and emitted by the plugin
|
|
71
|
+
generator. The deploy loop looks up each component's hint by `Spec.name`
|
|
72
|
+
and forwards it into that component's `make`. See
|
|
73
|
+
docs/plans/configurable-component-runtime-resources.md. */
|
|
74
|
+
~componentRuntime: dict<RuntimeHints.t>=?,
|
|
69
75
|
~uiFragments: Reventless.Plugin.uiFragmentManifest=?,
|
|
70
76
|
~pluginStructure: Reventless.Plugin.pluginStructure=?,
|
|
71
77
|
~opts: Pulumi.ComponentResource.options=?,
|
|
@@ -40,6 +40,7 @@ module type T = {
|
|
|
40
40
|
a cross-partition scalar command tag fans out into its own single-tag
|
|
41
41
|
decision-read clause. */
|
|
42
42
|
~crossPartitionTagKeys: array<string>=?,
|
|
43
|
+
~runtime: RuntimeHints.t=?,
|
|
43
44
|
~opts: Pulumi.ComponentResource.options=?,
|
|
44
45
|
) => component
|
|
45
46
|
}
|
package/src/components/Task.res
CHANGED
|
@@ -83,6 +83,7 @@ module type T = {
|
|
|
83
83
|
~queryEngine: Reventless.QueryEngine.operations,
|
|
84
84
|
~resourceNaming: ResourceNaming.operations,
|
|
85
85
|
~allAggregates: dict<Aggregate.outputs>,
|
|
86
|
+
~runtime: RuntimeHints.t=?,
|
|
86
87
|
~opts: option<Pulumi.ComponentResource.options>,
|
|
87
88
|
) => component
|
|
88
89
|
let outputs: component => outputs
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Platform-supplied per-kind runtime floor for pods that are sized straight from
|
|
3
|
+
core's resolved value (extension-point and task pods).
|
|
4
|
+
|
|
5
|
+
`RuntimeHints.resolveMemory`/`resolveTimeout` take a `~default` that is the
|
|
6
|
+
per-kind floor an override can only raise (`Math.Int.max`). For EP/task pods that
|
|
7
|
+
default is a *deployment* fact — how big a pod this platform wants — not a domain
|
|
8
|
+
fact, so the platform (not a core constant) supplies it. Each platform binds this
|
|
9
|
+
module at the seam where it applies the core `ExtensionPoint_Builder.Make` /
|
|
10
|
+
`Task_Builder.Make` functor; core feeds the two `let`s into `resolveMemory` /
|
|
11
|
+
`resolveTimeout` in place of the old hardcoded literals.
|
|
12
|
+
|
|
13
|
+
See docs/plans/runtime-hints-platform-supplied-defaults.md.
|
|
14
|
+
*/
|
|
15
|
+
module type T = {
|
|
16
|
+
let memorySize: int // MiB floor for this pod kind on this platform
|
|
17
|
+
let timeout: int // seconds
|
|
18
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Per-component runtime resource hints, declared once (portably) on the plugin's
|
|
3
|
+
composition surface (`plugin.json` `runtime` block) and threaded into each
|
|
4
|
+
component's `make` by the plugin deploy loop.
|
|
5
|
+
|
|
6
|
+
Both fields are optional; an omitted field falls through to the per-kind builder
|
|
7
|
+
default. The value is a *logical envelope* (working-set memory / wall-clock),
|
|
8
|
+
not a literal cap — each platform adapter maps it to its own resource model
|
|
9
|
+
(AWS Lambda `memorySize`/`timeout`, k8s pod `requests`/`limits`, local ignores).
|
|
10
|
+
|
|
11
|
+
See docs/plans/configurable-component-runtime-resources.md.
|
|
12
|
+
*/
|
|
13
|
+
type t = {
|
|
14
|
+
memorySize: option<int>,
|
|
15
|
+
timeout: option<int>,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** No hints — every field falls through to the per-kind builder default. */
|
|
19
|
+
let empty: t = {memorySize: None, timeout: None}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
Resolve a component's memory floor: the per-component override raises the
|
|
23
|
+
per-kind default, never lowers it (`Math.Int.max`). An absent override yields
|
|
24
|
+
the default unchanged.
|
|
25
|
+
*/
|
|
26
|
+
let resolveMemory = (hints: option<t>, ~default: int): int =>
|
|
27
|
+
switch hints {
|
|
28
|
+
| Some({memorySize: Some(m)}) => Math.Int.max(default, m)
|
|
29
|
+
| _ => default
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
Resolve a component's timeout: an explicit per-component timeout replaces the
|
|
34
|
+
default (no `max` — a longer default is not inherently safer). An absent
|
|
35
|
+
override yields the default unchanged.
|
|
36
|
+
*/
|
|
37
|
+
let resolveTimeout = (hints: option<t>, ~default: int): int =>
|
|
38
|
+
switch hints {
|
|
39
|
+
| Some({timeout: Some(t)}) => t
|
|
40
|
+
| _ => default
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function resolveMemory(hints, $$default) {
|
|
5
|
+
if (hints === undefined) {
|
|
6
|
+
return $$default;
|
|
7
|
+
}
|
|
8
|
+
let m = hints.memorySize;
|
|
9
|
+
if (m !== undefined) {
|
|
10
|
+
return Math.max($$default, m);
|
|
11
|
+
} else {
|
|
12
|
+
return $$default;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolveTimeout(hints, $$default) {
|
|
17
|
+
if (hints === undefined) {
|
|
18
|
+
return $$default;
|
|
19
|
+
}
|
|
20
|
+
let t = hints.timeout;
|
|
21
|
+
if (t !== undefined) {
|
|
22
|
+
return t;
|
|
23
|
+
} else {
|
|
24
|
+
return $$default;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let empty = {
|
|
29
|
+
memorySize: undefined,
|
|
30
|
+
timeout: undefined
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
empty,
|
|
35
|
+
resolveMemory,
|
|
36
|
+
resolveTimeout,
|
|
37
|
+
}
|
|
38
|
+
/* No side effect */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
module RH = RuntimeHints
|
|
4
|
+
|
|
5
|
+
// Precedence for per-component runtime hints (see
|
|
6
|
+
// docs/plans/configurable-component-runtime-resources.md): memory is a floor
|
|
7
|
+
// (override raises the per-kind default via Math.Int.max, never lowers it),
|
|
8
|
+
// timeout is a replace (an explicit value wins outright).
|
|
9
|
+
|
|
10
|
+
describe("RuntimeHints.resolveMemory", () => {
|
|
11
|
+
testSync("no hint falls through to the per-kind default", () => {
|
|
12
|
+
expect(RH.resolveMemory(None, ~default=1024))->toBe(1024)
|
|
13
|
+
})
|
|
14
|
+
testSync("an override above the default wins", () => {
|
|
15
|
+
expect(RH.resolveMemory(Some({memorySize: Some(2048), timeout: None}), ~default=1024))->toBe(2048)
|
|
16
|
+
})
|
|
17
|
+
testSync("an override below the default is clamped up to the floor", () => {
|
|
18
|
+
expect(RH.resolveMemory(Some({memorySize: Some(256), timeout: None}), ~default=1024))->toBe(1024)
|
|
19
|
+
})
|
|
20
|
+
testSync("an absent memorySize field falls through to the default", () => {
|
|
21
|
+
expect(RH.resolveMemory(Some({memorySize: None, timeout: Some(60)}), ~default=1024))->toBe(1024)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
describe("RuntimeHints.resolveTimeout", () => {
|
|
26
|
+
testSync("an explicit timeout replaces the default (no max)", () => {
|
|
27
|
+
expect(RH.resolveTimeout(Some({memorySize: None, timeout: Some(15)}), ~default=30))->toBe(15)
|
|
28
|
+
})
|
|
29
|
+
testSync("no hint falls through to the default", () => {
|
|
30
|
+
expect(RH.resolveTimeout(None, ~default=30))->toBe(30)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as DeployBootstrap$ReventlessInfra from "../src/components/DeployBootstrap.res.mjs";
|
|
4
|
-
|
|
5
|
-
globalThis.describe("DeployBootstrap", () => {
|
|
6
|
-
globalThis.describe("run with no registrations", () => {
|
|
7
|
-
globalThis.test("is a no-op that does not throw", () => {
|
|
8
|
-
DeployBootstrap$ReventlessInfra.reset();
|
|
9
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
10
|
-
DeployBootstrap$ReventlessInfra.run("PostDeploy");
|
|
11
|
-
globalThis.expect(true).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
globalThis.describe("registration order within a phase", () => {
|
|
15
|
-
globalThis.test("runs contributions in the order they were registered", () => {
|
|
16
|
-
DeployBootstrap$ReventlessInfra.reset();
|
|
17
|
-
let calls = [];
|
|
18
|
-
DeployBootstrap$ReventlessInfra.register(undefined, () => {
|
|
19
|
-
calls.push("a");
|
|
20
|
-
});
|
|
21
|
-
DeployBootstrap$ReventlessInfra.register(undefined, () => {
|
|
22
|
-
calls.push("b");
|
|
23
|
-
});
|
|
24
|
-
DeployBootstrap$ReventlessInfra.register(undefined, () => {
|
|
25
|
-
calls.push("c");
|
|
26
|
-
});
|
|
27
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
28
|
-
globalThis.expect(calls).toEqual([
|
|
29
|
-
"a",
|
|
30
|
-
"b",
|
|
31
|
-
"c"
|
|
32
|
-
]);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
globalThis.describe("phase isolation", () => {
|
|
36
|
-
globalThis.test("run(PreDeploy) fires only PreDeploy contributions", () => {
|
|
37
|
-
DeployBootstrap$ReventlessInfra.reset();
|
|
38
|
-
let calls = [];
|
|
39
|
-
DeployBootstrap$ReventlessInfra.register("PreDeploy", () => {
|
|
40
|
-
calls.push("pre");
|
|
41
|
-
});
|
|
42
|
-
DeployBootstrap$ReventlessInfra.register("PostDeploy", () => {
|
|
43
|
-
calls.push("post");
|
|
44
|
-
});
|
|
45
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
46
|
-
globalThis.expect(calls).toEqual(["pre"]);
|
|
47
|
-
DeployBootstrap$ReventlessInfra.run("PostDeploy");
|
|
48
|
-
globalThis.expect(calls).toEqual([
|
|
49
|
-
"pre",
|
|
50
|
-
"post"
|
|
51
|
-
]);
|
|
52
|
-
});
|
|
53
|
-
globalThis.test("defaults to PreDeploy when no phase is given", () => {
|
|
54
|
-
DeployBootstrap$ReventlessInfra.reset();
|
|
55
|
-
let calls = [];
|
|
56
|
-
DeployBootstrap$ReventlessInfra.register(undefined, () => {
|
|
57
|
-
calls.push("default");
|
|
58
|
-
});
|
|
59
|
-
DeployBootstrap$ReventlessInfra.run("PostDeploy");
|
|
60
|
-
globalThis.expect(calls).toEqual([]);
|
|
61
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
62
|
-
globalThis.expect(calls).toEqual(["default"]);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
globalThis.describe("idempotent run per phase", () => {
|
|
66
|
-
globalThis.test("running an already-run phase does nothing", () => {
|
|
67
|
-
DeployBootstrap$ReventlessInfra.reset();
|
|
68
|
-
let count = {
|
|
69
|
-
contents: 0
|
|
70
|
-
};
|
|
71
|
-
DeployBootstrap$ReventlessInfra.register(undefined, () => {
|
|
72
|
-
count.contents = count.contents + 1 | 0;
|
|
73
|
-
});
|
|
74
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
75
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
76
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
77
|
-
globalThis.expect(count.contents).toBe(1);
|
|
78
|
-
});
|
|
79
|
-
globalThis.test("a contribution registered after run does not fire", () => {
|
|
80
|
-
DeployBootstrap$ReventlessInfra.reset();
|
|
81
|
-
let calls = [];
|
|
82
|
-
DeployBootstrap$ReventlessInfra.register(undefined, () => {
|
|
83
|
-
calls.push("before");
|
|
84
|
-
});
|
|
85
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
86
|
-
DeployBootstrap$ReventlessInfra.register(undefined, () => {
|
|
87
|
-
calls.push("after");
|
|
88
|
-
});
|
|
89
|
-
DeployBootstrap$ReventlessInfra.run("PreDeploy");
|
|
90
|
-
globalThis.expect(calls).toEqual(["before"]);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
let DB;
|
|
96
|
-
|
|
97
|
-
export {
|
|
98
|
-
DB,
|
|
99
|
-
}
|
|
100
|
-
/* Not a pure module */
|