@reventlessdev/reventless-core 3.0.0-alpha.300 → 3.0.0-alpha.302
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 +32 -0
- package/README.md +3 -3
- package/package.json +10 -10
- package/src/{Behavior.res → Decision.res} +1 -1
- package/src/Message.res +1 -1
- package/src/ResourceAttribution.res +8 -8
- package/src/adapter/Auth/Auth_ActiveRole.res +1 -1
- package/src/adapter/Auth/Auth_RefusalVocabulary.res +1 -1
- package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +1 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder.res +2 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +10 -5
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +2 -2
- package/src/adapter/Runtime/EventCollectorRuntime_Builder.res +1 -1
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +8 -3
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder.res +1 -1
- package/src/adapter/Runtime/PluginRuntime_Builder.res +3 -3
- package/src/adapter/Runtime/Runtime.res +10 -9
- package/src/adapter/Runtime/TaskRuntime_Builder.res +1 -1
- package/src/adapter/RuntimeExtension/RuntimeExtension.res +3 -3
- package/src/admin/Platform_Admin_Structure.res +3 -3
- package/src/admin/Platform_Admin_Structure.res.mjs +1 -1
- package/src/admin/Platform_BakedManifest.res +8 -8
- package/src/admin/Platform_BakedManifest.res.mjs +2 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res +12 -12
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +11 -11
- package/src/admin/Platform_PluginStructuresApi.res +7 -7
- package/src/admin/Platform_PluginStructuresApi.res.mjs +4 -4
- package/src/admin/UiFragmentRegistry/StateChange/{UiFragmentRegistry_Behavior.res → UiFragmentRegistry_Decision.res} +1 -1
- package/src/admin/UiFragmentRegistry/StateChange/{UiFragmentRegistry_Behavior.res.mjs → UiFragmentRegistry_Decision.res.mjs} +1 -1
- package/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs +1 -4
- package/src/components/Aggregate/Aggregate_Builder.res +2 -2
- package/src/components/Aggregate/Aggregate_Builder.res.mjs +2 -2
- package/src/components/Aggregate/Aggregate_Callback.res +17 -17
- package/src/components/Aggregate/Aggregate_Callback.res.mjs +11 -11
- package/src/components/Api/ApiNoApiHelpers.res +1 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res +1 -1
- package/src/components/Api/SchemaType.res +1 -1
- package/src/components/Api/SuryToJsonSchema.res +1 -1
- package/src/components/CommandGenerator/CommandGenerator.res +1 -1
- package/src/components/CommandGenerator/CommandGenerator_Adapter.res +6 -1
- package/src/components/CommandTopic/CommandTopic.res +2 -2
- package/src/components/CommandTopic/CommandTopic_Adapter.res +1 -1
- package/src/components/CommandTopic/CommandTopic_Helpers.res +4 -4
- package/src/components/EventCollector/EventCollector.res +1 -1
- package/src/components/EventCollector/EventCollector_Adapter.res +1 -1
- package/src/components/EventLog/EventLog_Operations.res +2 -2
- package/src/components/Heartbeat/Heartbeat.res +1 -1
- package/src/components/ReadModel/ReadModel.res +1 -1
- package/src/components/StateChangeSlice/StateChangeSlice.res +1 -1
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res.mjs +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +9 -9
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -4
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +2 -2
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +2 -2
- package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +1 -1
- package/src/components/Task/Task_Adapter.res +1 -1
- package/src/components/Task/Task_Builder.res +1 -1
- package/src/plugin/component/Plugin_Builder.res +7 -7
- package/src/plugin/component/Plugin_Builder.res.mjs +3 -3
- package/src/plugin/component/Plugin_BuiltHook.res +1 -1
- package/src/plugin/component/Plugin_Structure.res +19 -19
- package/src/plugin/component/Plugin_Structure.res.mjs +8 -8
- package/src/plugin/lifecycle/{PluginBehavior.res → PluginDecision.res} +4 -4
- package/src/plugin/lifecycle/{PluginBehavior.res.mjs → PluginDecision.res.mjs} +1 -1
- package/tests/admin/AdminApiSchemaDriftTest.res +8 -8
- package/tests/admin/AdminApiSchemaDriftTest.res.mjs +54 -54
- package/tests/admin/Platform_Admin_StructureTest.res +10 -10
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +5 -5
- package/tests/admin/Platform_BakedManifestTest.res +7 -7
- package/tests/admin/Platform_BakedManifestTest.res.mjs +9 -9
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +11 -11
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +5 -5
- package/tests/admin/Platform_PluginStructuresApiTest.res +3 -3
- package/tests/aggregate/AggregateCacheTest.res +4 -4
- package/tests/aggregate/AggregateCacheTest.res.mjs +2 -2
- package/tests/aggregate/AggregateCallbackTest.res +2 -2
- package/tests/aggregate/AggregateCallbackTest.res.mjs +1 -1
- package/tests/aggregate/AggregateConflictTest.res +4 -4
- package/tests/aggregate/AggregateConflictTest.res.mjs +2 -2
- package/tests/aggregate/AggregateFixtures.res +5 -5
- package/tests/aggregate/AggregateFixtures.res.mjs +2 -2
- package/tests/aggregate/AggregateRejectionTest.res +1 -1
- package/tests/aggregate/AggregateSnapshotTest.res +7 -7
- package/tests/aggregate/AggregateSnapshotTest.res.mjs +4 -4
- package/tests/api/PsInternalFieldsReadModel.res +1 -1
- package/tests/api/TaggedUnionSchemaTest.res +1 -1
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res +1 -1
- package/tests/commandgenerator/CommandGeneratorFixtures.res +2 -2
- package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +2 -2
- package/tests/commandgenerator/OwnerActingTest.res +4 -4
- package/tests/commandgenerator/OwnerActingTest.res.mjs +4 -4
- package/tests/commandgenerator/OwnerStampingTest.res +1 -1
- package/tests/commandtopic/CommandOutcomeHookTest.res +2 -2
- package/tests/commandtopic/CommandOutcomeHookTest.res.mjs +3 -3
- package/tests/dcb/DcbStateChangeSliceTest.res +5 -5
- package/tests/dcb/DcbStateChangeSliceTest.res.mjs +5 -5
- package/tests/message/MessageTest.res +1 -1
- package/tests/plugin/ManifestVisibilityTest.res +2 -2
- package/tests/plugin/PluginStructureAccessTest.res +1 -1
- package/tests/plugin/PluginStructureAccessTest.res.mjs +4 -4
- package/tests/plugin/PluginStructureTest.res +24 -24
- package/tests/plugin/PluginStructureTest.res.mjs +27 -27
- package/tests/plugin/StateView/PsAnnotatedView.res +1 -1
- package/tests/plugin/StateView/PsAnnotatedView.res.mjs +1 -4
- package/tests/plugin/StateView/PsAvailableProductsView.res.mjs +1 -4
- package/tests/plugin/StateView/PsCategoriesView.res.mjs +1 -4
- package/tests/plugin/StateView/PsCustomersView.res.mjs +1 -4
- package/tests/plugin/StateView/PsGatedView.res.mjs +1 -4
- package/tests/plugin/StateView/PsOrdersView.res.mjs +1 -4
- package/tests/plugin/StateView/PsShipmentsView.res.mjs +1 -4
- package/tests/projection/LifecycleTrailTest.res +1 -1
- /package/src/{Behavior.res.mjs → Decision.res.mjs} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,38 @@
|
|
|
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.302 (2026-10-21)
|
|
7
|
+
|
|
8
|
+
* feat!: a state view's row key is module Id ([08c9726](https://github.com/ReventlessDev/reventless-core/commit/08c972659a3d689a39fd5e2f2026597155e92210))
|
|
9
|
+
* feat!: a plugin structure's component definitions are read-side and write-side defs ([ea2ab69](https://github.com/ReventlessDev/reventless-core/commit/ea2ab69e928ba16d7e640e4c66ab0e9ba034fe5c))
|
|
10
|
+
|
|
11
|
+
### BREAKING CHANGES
|
|
12
|
+
|
|
13
|
+
* a state view spec declares its row key as module Id. A
|
|
14
|
+
hand-written module Key in a spec under StateView/ or StateViewStream/
|
|
15
|
+
fails the build with "A state view's row key is `module Id`, not
|
|
16
|
+
`module Key`".
|
|
17
|
+
* Plugin.queryableDef, Plugin.writableDef, their schemas,
|
|
18
|
+
isPublicQueryable and encodeQueryableDef no longer exist; a reader names
|
|
19
|
+
readSideDef, writeSideDef, isPublicReadSide and encodeReadSideDef. There
|
|
20
|
+
are no aliases, so the compiler lists every reader.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# 3.0.0-alpha.301 (2026-10-21)
|
|
25
|
+
|
|
26
|
+
* feat!: the write-side logic module is called the decision, and a codemod moves an app over ([cb573ff](https://github.com/ReventlessDev/reventless-core/commit/cb573ff5b928c7d7f3e75988dfaa65066f8c95eb))
|
|
27
|
+
|
|
28
|
+
### BREAKING CHANGES
|
|
29
|
+
|
|
30
|
+
* <Spec>_Behavior.res, @@reventless.behavior,
|
|
31
|
+
Reventless.Behavior.T, StateChangeSlice.Behavior, Behavior_GWT and
|
|
32
|
+
thenRefused no longer exist. Run `npx reventless-rename-to-decision` at an
|
|
33
|
+
app's root, then rebuild; comments are not rewritten. The PPX must be the
|
|
34
|
+
matching release.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
6
38
|
# 3.0.0-alpha.300 (2026-10-19)
|
|
7
39
|
|
|
8
40
|
**Note:** Version bump only for package @reventlessdev/reventless-core
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
The **provider-agnostic core** of [Reventless](https://docs.reventless.dev) — a
|
|
11
11
|
spec-driven, event-sourced CQRS framework written in [ReScript](https://rescript-lang.org).
|
|
12
12
|
This package is the runtime you model your domain against: the type-safe building blocks
|
|
13
|
-
for commands, events, projections, and the
|
|
13
|
+
for commands, events, projections, and the decisions that evolve state — with **no** cloud
|
|
14
14
|
or storage dependency. The provider-specific adapters live in separate packages
|
|
15
15
|
(see [Where it fits](#where-it-fits)).
|
|
16
16
|
|
|
@@ -19,8 +19,8 @@ or storage dependency. The provider-specific adapters live in separate packages
|
|
|
19
19
|
ReScript modules, consumed by adding the package to your `rescript.json` `dependencies`:
|
|
20
20
|
|
|
21
21
|
- **`Message`** — command / event message contracts and their service interface.
|
|
22
|
-
- **`
|
|
23
|
-
(`evolve(state, event) => state`)
|
|
22
|
+
- **`Decision`** — the write side's decision module type (the decider pattern):
|
|
23
|
+
event-sourced state transitions (`evolve(state, event) => state`) and `decide`.
|
|
24
24
|
- **`Projection`** / **`ProjectionMapper`** — read models projected from event streams.
|
|
25
25
|
- **`Mapper`** / **`Mapper1toN`** / **`MapperNto1`** — cross-component event translation
|
|
26
26
|
(encode/decode between a source and a target contract).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.302",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -28,22 +28,22 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"sury": "11.0.0",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
|
-
"@reventlessdev/rescript-effect": "1.0.0-alpha.
|
|
32
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.22",
|
|
33
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.12",
|
|
34
|
-
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.22",
|
|
31
|
+
"@reventlessdev/rescript-effect": "1.0.0-alpha.1",
|
|
35
32
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.15",
|
|
33
|
+
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.23",
|
|
34
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.13",
|
|
35
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.23",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
|
|
37
|
+
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.24",
|
|
37
38
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
|
|
38
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
39
|
-
"@reventlessdev/reventless-interop": "3.0.0-alpha.
|
|
40
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
41
|
-
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.23"
|
|
39
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.199",
|
|
40
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.42",
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.171"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "12.3.1",
|
|
45
45
|
"sury-ppx": "11.0.0",
|
|
46
|
-
"@reventlessdev/reventless-ppx": "1.0.0-alpha.
|
|
46
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.104"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"rescript": "^12.3.1"
|
package/src/Message.res
CHANGED
|
@@ -308,7 +308,7 @@ let metaKeys = [
|
|
|
308
308
|
|
|
309
309
|
`StoredEvent` is the logical shape (nested `meta:meta`). The on-disk shape is
|
|
310
310
|
a flat top-level dict so DynamoDB GSIs can project individual meta attributes
|
|
311
|
-
(the §1.2/§3.2
|
|
311
|
+
(the §1.2/§3.2 behavior today, preserved here). These two helpers are the
|
|
312
312
|
single bridge between the two — used by both EventLog (aggregate) and
|
|
313
313
|
DcbEventLog (DCB) storage paths.
|
|
314
314
|
*/
|
|
@@ -7,11 +7,11 @@ drifting into private spellings of the same fact.
|
|
|
7
7
|
|
|
8
8
|
The three facts are deliberately independent:
|
|
9
9
|
|
|
10
|
-
- `ComponentType.t` — the *
|
|
10
|
+
- `ComponentType.t` — the *modeling* kind (Aggregate, StateViewSlice, …): what
|
|
11
11
|
the element IS in the domain model.
|
|
12
|
-
- `Role.t` — the *deployment
|
|
13
|
-
this particular resource does for that element. One component owns
|
|
14
|
-
|
|
12
|
+
- `Role.t` — the *deployment role* (EventLog, QueryDb, Runtime, …): what job
|
|
13
|
+
this particular resource does for that element. One component owns resources in
|
|
14
|
+
several roles, so kind alone can't identify a resource.
|
|
15
15
|
- `Scope.t` — the level the resource is attributed to: an individual component,
|
|
16
16
|
a plugin's shared substrate, or platform substrate.
|
|
17
17
|
|
|
@@ -40,8 +40,8 @@ module Scope = {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
The deployment
|
|
44
|
-
for support
|
|
43
|
+
The deployment role a resource plays. `Other(string)` is the escape hatch
|
|
44
|
+
for support roles and future ones (same pattern as `Monitoring.unitKind`).
|
|
45
45
|
*/
|
|
46
46
|
module Role = {
|
|
47
47
|
type t =
|
|
@@ -105,11 +105,11 @@ module Role = {
|
|
|
105
105
|
/**
|
|
106
106
|
The model component a provisioned resource belongs to.
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
Role builders (EventLog, QueryDb, CommandTopic, EventTopic, EventCollector) are
|
|
109
109
|
shared building blocks applied as functors by many different component kinds — a
|
|
110
110
|
QueryDb is instantiated by ReadModel, StateViewSlice, AutomationSlice, both
|
|
111
111
|
translation slices and Counter. The adapter that provisions the physical resource
|
|
112
|
-
sits at the bottom of that chain and would otherwise only know its own
|
|
112
|
+
sits at the bottom of that chain and would otherwise only know its own role, so
|
|
113
113
|
`kind` would collapse onto `role` and the owning component would be unrecoverable.
|
|
114
114
|
The owning builder passes this record down instead.
|
|
115
115
|
|
|
@@ -69,7 +69,7 @@ boundary — and a table copied into two test suites is exactly the drift it exi
|
|
|
69
69
|
to prevent.
|
|
70
70
|
|
|
71
71
|
`expected` is the group set the minted token must carry. `None` means the request
|
|
72
|
-
must be **refused** rather than
|
|
72
|
+
must be **refused** rather than honored, ignored, or reduced to the empty set —
|
|
73
73
|
the distinction §3 turns on. A path with no client to refuse (the trigger, reading
|
|
74
74
|
stored state) resolves `None` by minting `membership` and marking
|
|
75
75
|
[staleRoleClaim]; a path answering a live request refuses it.
|
|
@@ -120,7 +120,7 @@ Classify a refusal without knowing which adapter produced it.
|
|
|
120
120
|
|
|
121
121
|
Pass what the response actually carried: its status, the `errorType` of a GraphQL
|
|
122
122
|
error if there was one, and `extensions.code` if there was one. Returns `None`
|
|
123
|
-
when the response is not a refusal this contract
|
|
123
|
+
when the response is not a refusal this contract recognizes — which includes a
|
|
124
124
|
successful response, so a caller must not read `None` as "allowed".
|
|
125
125
|
|
|
126
126
|
Ordering matters and is the point of the function. The transport status is
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
Deploy-time hook: every event log announces itself as it is provisioned, so an
|
|
3
|
-
extension can attach
|
|
3
|
+
extension can attach behavior to it inside the program that owns it.
|
|
4
4
|
|
|
5
5
|
No-op by default. Zero behavioral change unless an extension registers a backend
|
|
6
6
|
via `use` before the platform/plugin build. The seam fires once per log at
|
|
@@ -10,12 +10,12 @@ module type T = {
|
|
|
10
10
|
CommandGenerator.component,
|
|
11
11
|
>
|
|
12
12
|
let forCommandTopic: Runtime.forComponent<
|
|
13
|
-
Runtime.
|
|
13
|
+
Runtime.effectInvocationHandler<CommandTopicChannel.callbackEvent, context, unit, string>,
|
|
14
14
|
runtimeParts,
|
|
15
15
|
CommandTopic.component<'op>,
|
|
16
16
|
>
|
|
17
17
|
let forEventCollector: Runtime.forEventCollector<
|
|
18
|
-
Runtime.
|
|
18
|
+
Runtime.effectInvocationHandler<EventCollectorChannel.callbackEvent, context, unit, string>,
|
|
19
19
|
EventCollector.component,
|
|
20
20
|
>
|
|
21
21
|
let finish: unit => unit
|
|
@@ -24,16 +24,21 @@ module Make = (
|
|
|
24
24
|
memorySize: int,
|
|
25
25
|
timeout: int,
|
|
26
26
|
}
|
|
27
|
-
type
|
|
27
|
+
type effectInvocationHandler = Runtime.effectInvocationHandler<
|
|
28
|
+
RuntimeEnvironment.event,
|
|
29
|
+
context,
|
|
30
|
+
unit,
|
|
31
|
+
string,
|
|
32
|
+
>
|
|
28
33
|
|
|
29
34
|
// Event collectors nested under this aggregate belong to their own element (a
|
|
30
35
|
// ReadModel, an EventMapper, …), so they carry the comp resolved at registration
|
|
31
36
|
// time rather than the hosting aggregate's.
|
|
32
|
-
type registeredHandler = {comp: string, handler:
|
|
37
|
+
type registeredHandler = {comp: string, handler: effectInvocationHandler}
|
|
33
38
|
|
|
34
39
|
let runtimeSpecs: dict<runtimeSpec> = Dict.make()
|
|
35
40
|
let commandGeneratorHandlers: dict<CommandGenerator.effectEventHandler<context>> = Dict.make()
|
|
36
|
-
let commandTopicHandlers: dict<
|
|
41
|
+
let commandTopicHandlers: dict<effectInvocationHandler> = Dict.make()
|
|
37
42
|
let eventCollectorHandlers: dict<array<registeredHandler>> = Dict.make()
|
|
38
43
|
|
|
39
44
|
let aggregateHandler = aggregateName =>
|
|
@@ -197,7 +202,7 @@ module Make = (
|
|
|
197
202
|
|
|
198
203
|
let forCommandTopic = (
|
|
199
204
|
~handler: Pulumi.Output.t<
|
|
200
|
-
Runtime.
|
|
205
|
+
Runtime.effectInvocationHandler<CommandTopicChannel.callbackEvent, context, unit, string>,
|
|
201
206
|
>,
|
|
202
207
|
~connect,
|
|
203
208
|
~memorySize=1024,
|
|
@@ -228,7 +233,7 @@ module Make = (
|
|
|
228
233
|
}
|
|
229
234
|
let forEventCollector = (
|
|
230
235
|
~handler: Pulumi.Output.t<
|
|
231
|
-
Runtime.
|
|
236
|
+
Runtime.effectInvocationHandler<EventCollectorChannel.callbackEvent, context, unit, string>,
|
|
232
237
|
>,
|
|
233
238
|
~eventTopics: EventTopic.allOutputs,
|
|
234
239
|
~resources: array<ReventlessInfra.Adapter.resource>,
|
|
@@ -45,7 +45,7 @@ module Make = (
|
|
|
45
45
|
}
|
|
46
46
|
let forCommandTopic = (
|
|
47
47
|
~handler: Pulumi.Output.t<
|
|
48
|
-
Runtime.
|
|
48
|
+
Runtime.effectInvocationHandler<CommandTopicChannel.callbackEvent, context, unit, string>,
|
|
49
49
|
>,
|
|
50
50
|
~connect,
|
|
51
51
|
~memorySize=1024,
|
|
@@ -76,7 +76,7 @@ module Make = (
|
|
|
76
76
|
|
|
77
77
|
let forEventCollector = (
|
|
78
78
|
~handler: Pulumi.Output.t<
|
|
79
|
-
Runtime.
|
|
79
|
+
Runtime.effectInvocationHandler<EventCollectorChannel.callbackEvent, context, unit, string>,
|
|
80
80
|
>,
|
|
81
81
|
~eventTopics: EventTopic.allOutputs,
|
|
82
82
|
~resources: array<ReventlessInfra.Adapter.resource>,
|
|
@@ -4,7 +4,7 @@ module type T = {
|
|
|
4
4
|
module EventCollectorChannel: EventCollector_Adapter.Channel
|
|
5
5
|
|
|
6
6
|
let forEventCollector: Runtime.forEventCollector<
|
|
7
|
-
Runtime.
|
|
7
|
+
Runtime.effectInvocationHandler<EventCollectorChannel.callbackEvent, context, unit, string>,
|
|
8
8
|
EventCollector.component,
|
|
9
9
|
>
|
|
10
10
|
let finish: unit => unit
|
|
@@ -33,12 +33,17 @@ module Make = (
|
|
|
33
33
|
maxMemorySize: 0,
|
|
34
34
|
maxTimeout: 0,
|
|
35
35
|
})
|
|
36
|
-
type
|
|
36
|
+
type effectInvocationHandler = Runtime.effectInvocationHandler<
|
|
37
|
+
RuntimeEnvironment.event,
|
|
38
|
+
context,
|
|
39
|
+
unit,
|
|
40
|
+
string,
|
|
41
|
+
>
|
|
37
42
|
// `comp` is the *element's* log component, resolved where the handler is registered
|
|
38
43
|
// (the event collector's own resource is in hand there). Keeping it next to the
|
|
39
44
|
// handler is what makes every collector hosted in this one runtime separable by
|
|
40
45
|
// field — the runtime-group name would be identical for all of them.
|
|
41
|
-
type registeredHandler = {comp: string, handler:
|
|
46
|
+
type registeredHandler = {comp: string, handler: effectInvocationHandler}
|
|
42
47
|
let eventCollectorHandlers: dict<array<registeredHandler>> = Dict.make()
|
|
43
48
|
|
|
44
49
|
let eventCollectorHandler = parentName =>
|
|
@@ -142,7 +147,7 @@ module Make = (
|
|
|
142
147
|
|
|
143
148
|
let forEventCollector = (
|
|
144
149
|
~handler: Pulumi.Output.t<
|
|
145
|
-
Runtime.
|
|
150
|
+
Runtime.effectInvocationHandler<EventCollectorChannel.callbackEvent, context, unit, string>,
|
|
146
151
|
>,
|
|
147
152
|
~eventTopics: EventTopic.allOutputs,
|
|
148
153
|
~resources: array<ReventlessInfra.Adapter.resource>,
|
|
@@ -5,7 +5,7 @@ module type T = {
|
|
|
5
5
|
|
|
6
6
|
let forCommandTopic: (
|
|
7
7
|
~handler: Pulumi.Output.t<
|
|
8
|
-
Runtime.
|
|
8
|
+
Runtime.effectInvocationHandler<CommandTopicChannel.callbackEvent, context, unit, string>,
|
|
9
9
|
>,
|
|
10
10
|
~connect: Runtime.connect<runtimeParts>,
|
|
11
11
|
~memorySize: int=?,
|
|
@@ -5,16 +5,16 @@ module type T = {
|
|
|
5
5
|
|
|
6
6
|
let registerPluginName: string => unit
|
|
7
7
|
let forPluginEventCollector: Runtime.forEventCollector<
|
|
8
|
-
Runtime.
|
|
8
|
+
Runtime.effectInvocationHandler<EventCollectorChannel.callbackEvent, context, unit, string>,
|
|
9
9
|
EventCollector.component,
|
|
10
10
|
>
|
|
11
11
|
let forPluginHeartbeat: Runtime.forComponent<
|
|
12
|
-
Runtime.
|
|
12
|
+
Runtime.invocationHandler<unit, context, unit>,
|
|
13
13
|
runtimeParts,
|
|
14
14
|
Heartbeat.component,
|
|
15
15
|
>
|
|
16
16
|
let forDcbCommandTopic: Runtime.forComponent<
|
|
17
|
-
Runtime.
|
|
17
|
+
Runtime.effectInvocationHandler<'callbackEvent, context, unit, string>,
|
|
18
18
|
runtimeParts,
|
|
19
19
|
CommandTopic.component<'op>,
|
|
20
20
|
>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
type
|
|
3
|
-
|
|
1
|
+
// Handlers of a platform invocation (a Lambda payload), not of domain events.
|
|
2
|
+
type invocationHandler<'payload, 'context, 'result> = ('payload, 'context) => promise<'result>
|
|
3
|
+
type effectInvocationHandler<'payload, 'context, 'result, 'error> = (
|
|
4
|
+
'payload,
|
|
4
5
|
'context,
|
|
5
6
|
) => Effect.t<'result, 'error, unit>
|
|
6
7
|
|
|
@@ -92,7 +93,7 @@ let runEffect = (
|
|
|
92
93
|
)
|
|
93
94
|
->Effect.runPromise
|
|
94
95
|
|
|
95
|
-
// Convert an
|
|
96
|
+
// Convert an effectInvocationHandler to an invocationHandler at build time, annotating the
|
|
96
97
|
// invocation on each call. Used by the single-component-per-Lambda strategies
|
|
97
98
|
// (Micro / PerAggregate / Plugin / PerEventCollector / PerExtensionPoint), which
|
|
98
99
|
// pass the Environment's `extractCorrelationId` / `extractCausationId` and the
|
|
@@ -105,8 +106,8 @@ let runEffectHandler = (
|
|
|
105
106
|
~extractRetryCount: option<'event => option<int>>=?,
|
|
106
107
|
~comp=?,
|
|
107
108
|
~pluginName=?,
|
|
108
|
-
handler:
|
|
109
|
-
):
|
|
109
|
+
handler: effectInvocationHandler<'event, 'context, 'result, 'error>,
|
|
110
|
+
): invocationHandler<'event, 'context, 'result> =>
|
|
110
111
|
(event, ctx) => {
|
|
111
112
|
let extract = (f, event) =>
|
|
112
113
|
switch f {
|
|
@@ -136,7 +137,7 @@ type environment<'parts> = {
|
|
|
136
137
|
|
|
137
138
|
type environmentMaker<'event, 'context, 'result, 'parts> = (
|
|
138
139
|
~name: string,
|
|
139
|
-
~handler: Pulumi.Output.t<
|
|
140
|
+
~handler: Pulumi.Output.t<invocationHandler<'event, 'context, 'result>>,
|
|
140
141
|
~memorySize: int=?,
|
|
141
142
|
~timeout: int=?,
|
|
142
143
|
~opts: Pulumi.ComponentResource.options=?,
|
|
@@ -154,8 +155,8 @@ module type Environment = {
|
|
|
154
155
|
// `None` where the transport carries no such notion (see each implementation).
|
|
155
156
|
let extractSentTimestamp: event => option<float>
|
|
156
157
|
let extractRetryCount: event => option<int>
|
|
157
|
-
let asEventHandler: 'a =>
|
|
158
|
-
let asEffectHandler: 'a =>
|
|
158
|
+
let asEventHandler: 'a => invocationHandler<event, context, 'result>
|
|
159
|
+
let asEffectHandler: 'a => effectInvocationHandler<event, context, 'result, 'error>
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
type connect<'parts> = (~runtime: environment<'parts>) => unit
|
|
@@ -18,7 +18,7 @@ module type T = {
|
|
|
18
18
|
type runtimeParts
|
|
19
19
|
|
|
20
20
|
let forBucketCallback: (
|
|
21
|
-
~handler: Pulumi.Output.t<Runtime.
|
|
21
|
+
~handler: Pulumi.Output.t<Runtime.invocationHandler<callbackEvent, context, unit>>,
|
|
22
22
|
~connect: Runtime.connect<runtimeParts>,
|
|
23
23
|
~memorySize: int=?,
|
|
24
24
|
~timeout: int=?,
|
|
@@ -8,7 +8,7 @@ No-op by default. Zero behavioral change unless an extension registers one via
|
|
|
8
8
|
no code runs.
|
|
9
9
|
|
|
10
10
|
`Monitoring` and `EventLogProvisioning` are the deploy-time half of this shape —
|
|
11
|
-
they let an extension attach
|
|
11
|
+
they let an extension attach behavior to *what gets provisioned*. This is the
|
|
12
12
|
runtime half: *what runs inside it*. Without it,
|
|
13
13
|
`CommandGenerator_Callback.registerCommandInterceptor`,
|
|
14
14
|
`QueryDb_Callback.registerQueryInterceptor` and the two
|
|
@@ -34,7 +34,7 @@ to internals would make it a second, informal handler API — and the four hooks
|
|
|
34
34
|
exists to reach already receive their own per-request payloads
|
|
35
35
|
(`commandInterceptor` gets `~componentName` / `~tag` / `~args` on every call).
|
|
36
36
|
|
|
37
|
-
`~runtimeKind` is the
|
|
37
|
+
`~runtimeKind` is the modeling kind of the component the runtime runs — the same
|
|
38
38
|
`ComponentType.t` every AWS runtime builder already passes to
|
|
39
39
|
`RuntimeEnvironment_Lambda.makeFromCodeAsset`, so there is no third taxonomy to
|
|
40
40
|
keep in step with `Monitoring.unitKind` and `ResourceAttribution.Role`.
|
|
@@ -67,7 +67,7 @@ runtime handles its first request.
|
|
|
67
67
|
seam reachable in a *deployed* runtime: registering a first-class module only
|
|
68
68
|
populates the deploy program's process, and the Lambda is a different one, so the
|
|
69
69
|
framework bundles the module's package into the code archive and names the module
|
|
70
|
-
in the runtime's config for the entry shell to import. Spec and
|
|
70
|
+
in the runtime's config for the entry shell to import. Spec and decision modules
|
|
71
71
|
are carried into runtimes the same way. The convention matches
|
|
72
72
|
`@@reventless.spec`'s injected `let moduleUrl`, so a ReScript extension can lift
|
|
73
73
|
it from there.
|
|
@@ -42,7 +42,7 @@ let pluginCommands: array<commandDef> = Plugin_Structure.extractCommandDefs(
|
|
|
42
42
|
commandSchema,
|
|
43
43
|
)
|
|
44
44
|
|
|
45
|
-
let pluginAggregate:
|
|
45
|
+
let pluginAggregate: writeSideDef = {
|
|
46
46
|
name: "Plugin",
|
|
47
47
|
commands: pluginCommands,
|
|
48
48
|
producedEventTypes: [],
|
|
@@ -59,8 +59,8 @@ let pluginAggregate: writableDef = {
|
|
|
59
59
|
|
|
60
60
|
// Functions, not values: the view's access keys are the group the platform maps
|
|
61
61
|
// the administrator role to, and this module is evaluated before a root maps it.
|
|
62
|
-
let pluginReadModel = ():
|
|
63
|
-
Plugin_Structure.
|
|
62
|
+
let pluginReadModel = (): readSideDef =>
|
|
63
|
+
Plugin_Structure.readSideDefFromSpec(
|
|
64
64
|
~plugin=pluginId,
|
|
65
65
|
~name=PluginsReadModelSpec.name,
|
|
66
66
|
~stateSchema=PluginsReadModelSpec.stateSchema->S.castToUnknown,
|
|
@@ -33,7 +33,7 @@ let pluginAggregate = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
function pluginReadModel() {
|
|
36
|
-
return Plugin_Structure$ReventlessCore.
|
|
36
|
+
return Plugin_Structure$ReventlessCore.readSideDefFromSpec(pluginId, PluginsReadModelSpec$ReventlessCore.name, PluginsReadModelSpec$ReventlessCore.stateSchema, Authorization$Reventless.named(PluginsReadModelSpec$ReventlessCore.authorization), undefined, undefined, ["Plugin"], undefined);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function structure() {
|
|
@@ -57,7 +57,7 @@ let describe = (e: error): string =>
|
|
|
57
57
|
// gives. Unlike the other two, what it selects is not filtered here — there is
|
|
58
58
|
// nothing in a `pluginStructure` to filter, because the pages do not exist until
|
|
59
59
|
// a shell builds them. It travels to the shell instead, which is the only place
|
|
60
|
-
// that can
|
|
60
|
+
// that can honor it.
|
|
61
61
|
type selection = {
|
|
62
62
|
plugin: string,
|
|
63
63
|
views: option<array<string>>,
|
|
@@ -65,12 +65,12 @@ type selection = {
|
|
|
65
65
|
derived: option<array<string>>,
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
let publicViews = (def: pluginStructure): array<
|
|
68
|
+
let publicViews = (def: pluginStructure): array<readSideDef> =>
|
|
69
69
|
Array.concat(def.readModels, def.stateViewSlices)->Array.filter(
|
|
70
|
-
Platform_ComponentDefinitionsApi.
|
|
70
|
+
Platform_ComponentDefinitionsApi.isPublicReadSide,
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
let writeSides = (def: pluginStructure): array<
|
|
73
|
+
let writeSides = (def: pluginStructure): array<writeSideDef> =>
|
|
74
74
|
Array.concat(def.stateChangeSlices, def.aggregates)
|
|
75
75
|
|
|
76
76
|
let commandNames = (def: pluginStructure): array<string> =>
|
|
@@ -116,7 +116,7 @@ let isSelected = (selected: option<array<string>>, name: string): bool =>
|
|
|
116
116
|
// breaks the picker permanently in the one deployment shape with no fallback.
|
|
117
117
|
// Naming it discloses nothing: the domain API already serves that view to the same
|
|
118
118
|
// caller (Internal is a menu hint, not a boundary).
|
|
119
|
-
let referencedEntities = (~pluginName: string, kept: array<
|
|
119
|
+
let referencedEntities = (~pluginName: string, kept: array<writeSideDef>): array<string> =>
|
|
120
120
|
kept
|
|
121
121
|
->Array.flatMap(w => w.commands)
|
|
122
122
|
->Array.flatMap(c => c.references)
|
|
@@ -133,7 +133,7 @@ let curateStructure = (~pluginId: string, ~def: pluginStructure, sel: selection)
|
|
|
133
133
|
> =>
|
|
134
134
|
validate(~def, sel)->Result.map(() => {
|
|
135
135
|
let pluginName = Plugin.name(pluginId)
|
|
136
|
-
let keepCommands = (ws: array<
|
|
136
|
+
let keepCommands = (ws: array<writeSideDef>) =>
|
|
137
137
|
ws
|
|
138
138
|
->Array.map(w => {
|
|
139
139
|
...w,
|
|
@@ -147,9 +147,9 @@ let curateStructure = (~pluginId: string, ~def: pluginStructure, sel: selection)
|
|
|
147
147
|
let stateChangeSlices = def.stateChangeSlices->keepCommands
|
|
148
148
|
let aggregates = def.aggregates->keepCommands
|
|
149
149
|
let referenced = referencedEntities(~pluginName, Array.concat(stateChangeSlices, aggregates))
|
|
150
|
-
let keepViews = (qs: array<
|
|
150
|
+
let keepViews = (qs: array<readSideDef>) =>
|
|
151
151
|
qs->Array.filter(q =>
|
|
152
|
-
if Platform_ComponentDefinitionsApi.
|
|
152
|
+
if Platform_ComponentDefinitionsApi.isPublicReadSide(q) {
|
|
153
153
|
sel.views->isSelected(q.name)
|
|
154
154
|
} else {
|
|
155
155
|
referenced->Array.includes(q.name)
|
|
@@ -27,7 +27,7 @@ function describe(e) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function publicViews(def) {
|
|
30
|
-
return def.readModels.concat(def.stateViewSlices).filter(Platform_ComponentDefinitionsApi$ReventlessCore.
|
|
30
|
+
return def.readModels.concat(def.stateViewSlices).filter(Platform_ComponentDefinitionsApi$ReventlessCore.isPublicReadSide);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function writeSides(def) {
|
|
@@ -117,7 +117,7 @@ function curateStructure(pluginId, def, sel) {
|
|
|
117
117
|
let aggregates = keepCommands(def.aggregates);
|
|
118
118
|
let referenced = referencedEntities(pluginName, stateChangeSlices.concat(aggregates));
|
|
119
119
|
let keepViews = qs => qs.filter(q => {
|
|
120
|
-
if (Platform_ComponentDefinitionsApi$ReventlessCore.
|
|
120
|
+
if (Platform_ComponentDefinitionsApi$ReventlessCore.isPublicReadSide(q)) {
|
|
121
121
|
return isSelected(sel.views, q.name);
|
|
122
122
|
} else {
|
|
123
123
|
return referenced.includes(q.name);
|
|
@@ -92,7 +92,7 @@ let encodeCommandDef = (c: commandDef): JSON.t =>
|
|
|
92
92
|
|
|
93
93
|
// Internal ReadModels / StateViewSlices are carried in pluginStructure for developer
|
|
94
94
|
// tooling but must stay out of the deployed AutoUI — the menu, drill-down pages and
|
|
95
|
-
//
|
|
95
|
+
// read-side defs are all derived from this response, so filter them here.
|
|
96
96
|
//
|
|
97
97
|
// Hidden from the menu is not the same question as denied as a reference target: a
|
|
98
98
|
// denormalised lookup view is precisely the thing an author wants referenceable but
|
|
@@ -100,9 +100,9 @@ let encodeCommandDef = (c: commandDef): JSON.t =>
|
|
|
100
100
|
// (below) answers the second. Neither is a security boundary — an Internal view
|
|
101
101
|
// keeps its resolver and stays queryable, so naming it here publishes a fact the
|
|
102
102
|
// caller is already entitled to.
|
|
103
|
-
let
|
|
103
|
+
let isPublicReadSide = (q: readSideDef): bool => q.visibility != Some("Internal")
|
|
104
104
|
|
|
105
|
-
let
|
|
105
|
+
let encodeReadSideDef = (r: readSideDef): JSON.t =>
|
|
106
106
|
Dict.fromArray([
|
|
107
107
|
("name", JSON.Encode.string(r.name)),
|
|
108
108
|
("queryField", JSON.Encode.string(r.queryField)),
|
|
@@ -143,7 +143,7 @@ let encodeErrorDef = (e: errorDef): JSON.t =>
|
|
|
143
143
|
("references", e.references->Array.map(encodeFieldReference)->JSON.Encode.array),
|
|
144
144
|
])->JSON.Encode.object
|
|
145
145
|
|
|
146
|
-
let
|
|
146
|
+
let encodeWriteSideDef = (w: writeSideDef): JSON.t =>
|
|
147
147
|
Dict.fromArray([
|
|
148
148
|
("name", JSON.Encode.string(w.name)),
|
|
149
149
|
("commands", w.commands->Array.map(encodeCommandDef)->JSON.Encode.array),
|
|
@@ -254,26 +254,26 @@ let encodePluginStructureEntry = (
|
|
|
254
254
|
(
|
|
255
255
|
"readModels",
|
|
256
256
|
def.readModels
|
|
257
|
-
->Array.filter(
|
|
258
|
-
->Array.map(
|
|
257
|
+
->Array.filter(isPublicReadSide)
|
|
258
|
+
->Array.map(encodeReadSideDef)
|
|
259
259
|
->JSON.Encode.array,
|
|
260
260
|
),
|
|
261
261
|
(
|
|
262
262
|
"stateViewSlices",
|
|
263
263
|
def.stateViewSlices
|
|
264
|
-
->Array.filter(
|
|
265
|
-
->Array.map(
|
|
264
|
+
->Array.filter(isPublicReadSide)
|
|
265
|
+
->Array.map(encodeReadSideDef)
|
|
266
266
|
->JSON.Encode.array,
|
|
267
267
|
),
|
|
268
268
|
(
|
|
269
269
|
"internalQueryables",
|
|
270
270
|
Array.concat(def.readModels, def.stateViewSlices)
|
|
271
|
-
->Array.filter(q => !
|
|
272
|
-
->Array.map(
|
|
271
|
+
->Array.filter(q => !isPublicReadSide(q))
|
|
272
|
+
->Array.map(encodeReadSideDef)
|
|
273
273
|
->JSON.Encode.array,
|
|
274
274
|
),
|
|
275
|
-
("stateChangeSlices", def.stateChangeSlices->Array.map(
|
|
276
|
-
("aggregates", def.aggregates->Array.map(
|
|
275
|
+
("stateChangeSlices", def.stateChangeSlices->Array.map(encodeWriteSideDef)->JSON.Encode.array),
|
|
276
|
+
("aggregates", def.aggregates->Array.map(encodeWriteSideDef)->JSON.Encode.array),
|
|
277
277
|
(
|
|
278
278
|
"automationSlices",
|
|
279
279
|
def.automationSlices->Array.map(encodeAutomationSliceDef)->JSON.Encode.array,
|