@reventlessdev/reventless-infra 3.0.0-alpha.81
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 +709 -0
- package/LICENSE +202 -0
- package/package.json +35 -0
- package/rescript.json +28 -0
- package/src/adapter/Adapter.res +88 -0
- package/src/adapter/Adapter.res.mjs +29 -0
- package/src/components/Aggregate.res +73 -0
- package/src/components/Aggregate.res.mjs +2 -0
- package/src/components/Api.res +124 -0
- package/src/components/Api.res.mjs +31 -0
- package/src/components/Api_Adapter.res +39 -0
- package/src/components/Api_Adapter.res.mjs +2 -0
- package/src/components/AutomationSlice.res +48 -0
- package/src/components/AutomationSlice.res.mjs +2 -0
- package/src/components/CommandGenerator.res +8 -0
- package/src/components/CommandGenerator.res.mjs +2 -0
- package/src/components/CommandTopic.res +70 -0
- package/src/components/CommandTopic.res.mjs +2 -0
- package/src/components/Component.js +56 -0
- package/src/components/Component.mjs +10 -0
- package/src/components/Component.res +90 -0
- package/src/components/Component.res.mjs +57 -0
- package/src/components/Component.resi +21 -0
- package/src/components/Counter.res +81 -0
- package/src/components/Counter.res.mjs +2 -0
- package/src/components/DcbEventLog.res +115 -0
- package/src/components/DcbEventLog.res.mjs +2 -0
- package/src/components/EventCollector.res +19 -0
- package/src/components/EventCollector.res.mjs +2 -0
- package/src/components/EventLog.res +24 -0
- package/src/components/EventLog.res.mjs +2 -0
- package/src/components/EventMapper.res +37 -0
- package/src/components/EventMapper.res.mjs +2 -0
- package/src/components/EventTopic.res +51 -0
- package/src/components/EventTopic.res.mjs +2 -0
- package/src/components/Extension.res +64 -0
- package/src/components/Extension.res.mjs +2 -0
- package/src/components/ExtensionPoint.res +62 -0
- package/src/components/ExtensionPoint.res.mjs +2 -0
- package/src/components/Heartbeat.res +7 -0
- package/src/components/Heartbeat.res.mjs +2 -0
- package/src/components/InboundTranslationSlice.res +41 -0
- package/src/components/InboundTranslationSlice.res.mjs +2 -0
- package/src/components/OutboundTranslationSlice.res +44 -0
- package/src/components/OutboundTranslationSlice.res.mjs +2 -0
- package/src/components/Plugin.res +84 -0
- package/src/components/Plugin.res.mjs +2 -0
- package/src/components/QueryDb.res +48 -0
- package/src/components/QueryDb.res.mjs +33 -0
- package/src/components/ReadModel.res +50 -0
- package/src/components/ReadModel.res.mjs +2 -0
- package/src/components/Scheduler.res +32 -0
- package/src/components/Scheduler.res.mjs +2 -0
- package/src/components/StateChangeSlice.res +45 -0
- package/src/components/StateChangeSlice.res.mjs +2 -0
- package/src/components/StateViewSlice.res +38 -0
- package/src/components/StateViewSlice.res.mjs +2 -0
- package/src/components/Task.res +89 -0
- package/src/components/Task.res.mjs +2 -0
- package/src/types/ExtensionMapping.res +448 -0
- package/src/types/ExtensionMapping.res.mjs +252 -0
- package/src/types/ExtensionPointMapping.res +303 -0
- package/src/types/ExtensionPointMapping.res.mjs +116 -0
- package/src/types/Message.res +3 -0
- package/src/types/Message.res.mjs +2 -0
- package/src/types/NoEventMappings.res +25 -0
- package/src/types/NoEventMappings.res.mjs +17 -0
- package/src/types/Platform.res +279 -0
- package/src/types/Platform.res.mjs +2 -0
- package/src/types/PluginExtensionPointSpec.res +43 -0
- package/src/types/PluginExtensionPointSpec.res.mjs +204 -0
- package/src/types/ResourceNaming.res +12 -0
- package/src/types/ResourceNaming.res.mjs +2 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Deploy-time outputs produced when an `AutomationSlice` is provisioned.
|
|
3
|
+
|
|
4
|
+
- `resources` — the underlying infrastructure (e.g. SQS subscription)
|
|
5
|
+
- `queryDb` — the DynamoDB table for the TODO list
|
|
6
|
+
*/
|
|
7
|
+
type outputs = {
|
|
8
|
+
resources: array<Adapter.resource>,
|
|
9
|
+
queryDb: QueryDb.outputs,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
Runtime operations exposed by an `AutomationSlice` component.
|
|
14
|
+
|
|
15
|
+
- `enqueueEvent` — push events into the slice's projection queue
|
|
16
|
+
- `processPending` — manually trigger Phase 2 (useful in tests and for heartbeat)
|
|
17
|
+
*/
|
|
18
|
+
type operations = {
|
|
19
|
+
enqueueEvent: EventCollector.enqueueEvent,
|
|
20
|
+
processPending: unit => promise<unit>,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
Module type produced by `Platform.AutomationSlice.Make(Spec)`.
|
|
25
|
+
|
|
26
|
+
@example
|
|
27
|
+
```rescript
|
|
28
|
+
module ShipOrderSlice = Platform.AutomationSlice.Make(ShipOrder)
|
|
29
|
+
let slice = ShipOrderSlice.make(~dcbEventLog=log, ~publishJsons=publishJsonsOutput)
|
|
30
|
+
```
|
|
31
|
+
*/
|
|
32
|
+
type t
|
|
33
|
+
|
|
34
|
+
module type T = {
|
|
35
|
+
module Spec: Reventless.AutomationSlice.Spec
|
|
36
|
+
module Automation: Reventless.AutomationSlice.Automation with module Spec := Spec
|
|
37
|
+
type component = Component.t<t, outputs, operations>
|
|
38
|
+
let queryDbName: string
|
|
39
|
+
/** Names of all sources this slice consumes (deduplicated). Used by
|
|
40
|
+
`Plugin_Builder` for the source-name fail-fast assembly check. */
|
|
41
|
+
let sourceNames: array<string>
|
|
42
|
+
let make: (
|
|
43
|
+
~allEventTopics: EventTopic.allOutputs,
|
|
44
|
+
~publishJsons: Pulumi.Output.t<CommandTopic.publishJsons>,
|
|
45
|
+
~context: Reventless.AutomationSlice.context,
|
|
46
|
+
~opts: Pulumi.ComponentResource.options=?,
|
|
47
|
+
) => component
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy-time outputs produced when a `CommandGenerator` is provisioned.
|
|
3
|
+
*
|
|
4
|
+
* The command generator is an internal component that translates raw JSON
|
|
5
|
+
* command messages from the command topic into typed domain commands and
|
|
6
|
+
* dispatches them to the aggregate handler.
|
|
7
|
+
*/
|
|
8
|
+
type outputs = {resources: array<Adapter.resource>}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Module type for an aggregate's command topic specification.
|
|
3
|
+
|
|
4
|
+
`CommandTopic.T` is used by the framework to identify the command channel
|
|
5
|
+
(e.g. an SQS FIFO queue) and validate that published commands match the
|
|
6
|
+
aggregate's command schema.
|
|
7
|
+
*/
|
|
8
|
+
module type T = {
|
|
9
|
+
module Id: Reventless.Id.T
|
|
10
|
+
|
|
11
|
+
/** Logical name of the aggregate or component owning this command topic. */
|
|
12
|
+
let name: string
|
|
13
|
+
|
|
14
|
+
/** The command type published to this topic. Must carry `@schema` for serialization. */
|
|
15
|
+
@schema
|
|
16
|
+
type command
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
Deploy-time outputs produced when a `CommandTopic` is provisioned.
|
|
21
|
+
Contains the underlying queue / messaging infrastructure resources.
|
|
22
|
+
*/
|
|
23
|
+
type outputs = {resources: array<Adapter.resource>}
|
|
24
|
+
|
|
25
|
+
/** A dictionary of command topic outputs keyed by aggregate name. */
|
|
26
|
+
type allOutputs = dict<outputs>
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
Publishes an array of serialized command envelopes to this command topic.
|
|
30
|
+
|
|
31
|
+
Commands are batched for efficiency. Each `Message.commandJson` carries
|
|
32
|
+
the aggregate ID, metadata, raw JSON payload, and optional delivery delay.
|
|
33
|
+
|
|
34
|
+
@example
|
|
35
|
+
```rescript
|
|
36
|
+
await ops.publishJsons([{id: "cat-1", meta, commandJson: json}])
|
|
37
|
+
```
|
|
38
|
+
*/
|
|
39
|
+
type publishJsons = array<Reventless.Message.commandJson> => promise<unit>
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
Publishes a stream of serialized command envelopes as an `Effect.t`.
|
|
43
|
+
Use this for high-throughput or streaming command pipelines.
|
|
44
|
+
*/
|
|
45
|
+
type publishJsonsStream = Stream.t<Reventless.Message.commandJson, string, unit> => Effect.t<unit, string, unit>
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
A command together with an idempotency reference string.
|
|
49
|
+
|
|
50
|
+
Used by the extension point runtime to track which commands have already
|
|
51
|
+
been processed and avoid duplicate dispatch.
|
|
52
|
+
*/
|
|
53
|
+
type topicItem<'command> = {
|
|
54
|
+
command: 'command,
|
|
55
|
+
reference: string,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
A handler that processes a stream of typed topic items and returns an Effect
|
|
60
|
+
producing per-item results.
|
|
61
|
+
|
|
62
|
+
`commandsHandler<JSON.t>` is the JSON-level variant used for routing;
|
|
63
|
+
`commandsHandler<Reventless.Message.command'<Id.t, command>>` is the decoded variant
|
|
64
|
+
used by aggregate and slice callbacks.
|
|
65
|
+
*/
|
|
66
|
+
type commandsHandler<'command> = Stream.t<topicItem<'command>, string, unit> => Effect.t<
|
|
67
|
+
array<result<string, string>>,
|
|
68
|
+
string,
|
|
69
|
+
unit,
|
|
70
|
+
>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
let ComponentMjs = require("./Component.mjs").default;
|
|
5
|
+
|
|
6
|
+
let _outputsStore = new WeakMap();
|
|
7
|
+
|
|
8
|
+
let _operationsStore = new WeakMap();
|
|
9
|
+
|
|
10
|
+
function outputs(self) {
|
|
11
|
+
return _outputsStore.get(self);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function wrappedOutputs(component) {
|
|
15
|
+
return component.apply(outputs);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function setOperations(self, ops) {
|
|
19
|
+
_operationsStore.set(self, ops);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function operations(self) {
|
|
23
|
+
return _operationsStore.get(self);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function make(prim0, prim1, prim2, prim3) {
|
|
27
|
+
return new ComponentMjs(prim0, prim1, prim2, prim3);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function registerOutputs(self, outputs) {
|
|
31
|
+
return self.registerOutputs(outputs);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function setOutputs(self, outputs) {
|
|
35
|
+
_outputsStore.set(self, outputs);
|
|
36
|
+
return self.registerOutputs({});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function toPulumiResource(prim) {
|
|
40
|
+
return prim;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function fromPulumiResource(prim) {
|
|
44
|
+
return prim;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
exports.registerOutputs = registerOutputs;
|
|
48
|
+
exports.setOutputs = setOutputs;
|
|
49
|
+
exports.outputs = outputs;
|
|
50
|
+
exports.wrappedOutputs = wrappedOutputs;
|
|
51
|
+
exports.setOperations = setOperations;
|
|
52
|
+
exports.operations = operations;
|
|
53
|
+
exports.toPulumiResource = toPulumiResource;
|
|
54
|
+
exports.fromPulumiResource = fromPulumiResource;
|
|
55
|
+
exports.make = make;
|
|
56
|
+
/* _outputsStore Not a pure module */
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
2
|
+
|
|
3
|
+
class Component extends pulumi.ComponentResource {
|
|
4
|
+
constructor(componentType, name, construct, opts, param1, param2, param3) {
|
|
5
|
+
super("reventless:" + componentType, name, {}, opts);
|
|
6
|
+
construct(this, name, param1, param2, param3);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default Component;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
A Reventless component — a typed wrapper around a Pulumi `ComponentResource`.
|
|
3
|
+
|
|
4
|
+
Type parameters:
|
|
5
|
+
- `'component` — the concrete component module type (e.g. `Aggregate.T`)
|
|
6
|
+
- `'outputs` — the deploy-time outputs record (infrastructure references)
|
|
7
|
+
- `'operations` — the runtime operations record (async functions for Lambda handlers)
|
|
8
|
+
|
|
9
|
+
Components are created at deploy time by a `Make` functor and carry two payloads:
|
|
10
|
+
- `outputs` — immediately available infrastructure references (queue ARNs, table names…)
|
|
11
|
+
- `operations` — an `Output.t`-wrapped set of async functions injected at runtime
|
|
12
|
+
*/
|
|
13
|
+
type t<'component, 'outputs, 'operations>
|
|
14
|
+
|
|
15
|
+
// Side-channel storage for outputs and operations.
|
|
16
|
+
// Using WeakMaps keyed by the component instance prevents Pulumi from
|
|
17
|
+
// serializing internal data (sury schemas, Spec modules) as ComponentResource
|
|
18
|
+
// properties in `pulumi stack output`.
|
|
19
|
+
type weakMap
|
|
20
|
+
@new external _makeWeakMap: unit => weakMap = "WeakMap"
|
|
21
|
+
@send external _get: (weakMap, t<'c, 'o, 'p>) => 'v = "get"
|
|
22
|
+
@send external _set: (weakMap, t<'c, 'o, 'p>, 'v) => unit = "set"
|
|
23
|
+
|
|
24
|
+
let _outputsStore = _makeWeakMap()
|
|
25
|
+
let _operationsStore = _makeWeakMap()
|
|
26
|
+
|
|
27
|
+
/** Access the deploy-time outputs record for this component. */
|
|
28
|
+
let outputs = (self: t<'component, 'outputs, 'operations>): 'outputs =>
|
|
29
|
+
_outputsStore->_get(self)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
Access the deploy-time outputs wrapped in an `Output.t` (resolved asynchronously).
|
|
33
|
+
Useful when you need to chain outputs into another `Output.apply`.
|
|
34
|
+
*/
|
|
35
|
+
let wrappedOutputs = component => component->Pulumi.Output.apply(component => component->outputs)
|
|
36
|
+
|
|
37
|
+
let setOperations = (
|
|
38
|
+
self: t<'component, 'outputs, 'operations>,
|
|
39
|
+
ops: Pulumi.Output.t<'operations>,
|
|
40
|
+
): unit => _operationsStore->_set(self, ops)
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
Access the runtime operations for this component.
|
|
44
|
+
|
|
45
|
+
Returns an `Output.t` that resolves to the runtime operations record once the
|
|
46
|
+
underlying infrastructure values are known. Use `TestRunner.resolve` in tests.
|
|
47
|
+
*/
|
|
48
|
+
let operations = (self: t<'component, 'outputs, 'operations>): Pulumi.Output.t<'operations> =>
|
|
49
|
+
_operationsStore->_get(self)
|
|
50
|
+
|
|
51
|
+
/** Cast to a plain Pulumi resource (e.g. to pass to `~opts` as a parent). */
|
|
52
|
+
external toPulumiResource: t<'component, 'outputs, 'operations> => Pulumi.Resource.t = "%identity"
|
|
53
|
+
external fromPulumiResource: Pulumi.Resource.t => t<'component, 'outputs, 'operations> = "%identity"
|
|
54
|
+
|
|
55
|
+
type constructed
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
Create a new Reventless component backed by a Pulumi `ComponentResource`.
|
|
59
|
+
|
|
60
|
+
- `componentType` — Pulumi type token (e.g. `"reventless:index:Aggregate"`)
|
|
61
|
+
- `name` — unique Pulumi resource name
|
|
62
|
+
- `construct` — a callback that builds child resources; called by the Pulumi runtime
|
|
63
|
+
- `opts` — optional Pulumi resource options (parent, provider, etc.)
|
|
64
|
+
*/
|
|
65
|
+
@module("./Component.mjs") @new
|
|
66
|
+
external make: (
|
|
67
|
+
~componentType: string,
|
|
68
|
+
~name: string,
|
|
69
|
+
~construct: 'construct,
|
|
70
|
+
~opts: option<Pulumi.ComponentResource.options>,
|
|
71
|
+
) => t<'component, 'outputs, 'operations> = "default"
|
|
72
|
+
|
|
73
|
+
@send
|
|
74
|
+
external _registerOutputs: (t<'component, 'outputs, 'operations>, 'a) => constructed =
|
|
75
|
+
"registerOutputs"
|
|
76
|
+
|
|
77
|
+
/** Register the component's outputs with Pulumi to mark it as constructed. */
|
|
78
|
+
let registerOutputs = (self, outputs) => self->_registerOutputs(outputs)
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
Set the component's outputs and mark the component as constructed.
|
|
82
|
+
|
|
83
|
+
Outputs are stored in a WeakMap (not on the ComponentResource instance) to prevent
|
|
84
|
+
Pulumi from serializing internal data in `pulumi stack output`. Cross-stack data
|
|
85
|
+
is exported explicitly via `Pulumi.export`.
|
|
86
|
+
*/
|
|
87
|
+
let setOutputs = (self, outputs) => {
|
|
88
|
+
_outputsStore->_set(self, outputs)
|
|
89
|
+
self->_registerOutputs(JSON.Encode.object(Dict.make()))
|
|
90
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import ComponentMjs from "./Component.mjs";
|
|
4
|
+
|
|
5
|
+
let _outputsStore = new WeakMap();
|
|
6
|
+
|
|
7
|
+
let _operationsStore = new WeakMap();
|
|
8
|
+
|
|
9
|
+
function outputs(self) {
|
|
10
|
+
return _outputsStore.get(self);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function wrappedOutputs(component) {
|
|
14
|
+
return component.apply(outputs);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function setOperations(self, ops) {
|
|
18
|
+
_operationsStore.set(self, ops);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function operations(self) {
|
|
22
|
+
return _operationsStore.get(self);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function make(prim0, prim1, prim2, prim3) {
|
|
26
|
+
return new ComponentMjs(prim0, prim1, prim2, prim3);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function registerOutputs(self, outputs) {
|
|
30
|
+
return self.registerOutputs(outputs);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function setOutputs(self, outputs) {
|
|
34
|
+
_outputsStore.set(self, outputs);
|
|
35
|
+
return self.registerOutputs({});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toPulumiResource(prim) {
|
|
39
|
+
return prim;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function fromPulumiResource(prim) {
|
|
43
|
+
return prim;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
registerOutputs,
|
|
48
|
+
setOutputs,
|
|
49
|
+
outputs,
|
|
50
|
+
wrappedOutputs,
|
|
51
|
+
setOperations,
|
|
52
|
+
operations,
|
|
53
|
+
toPulumiResource,
|
|
54
|
+
fromPulumiResource,
|
|
55
|
+
make,
|
|
56
|
+
}
|
|
57
|
+
/* _outputsStore Not a pure module */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type t<'component, 'outputs, 'operations>
|
|
2
|
+
|
|
3
|
+
type constructed
|
|
4
|
+
let registerOutputs: (t<'component, 'outputs, 'operations>, 'outputs) => constructed
|
|
5
|
+
let setOutputs: (t<'component, 'outputs, 'operations>, 'outputs) => constructed
|
|
6
|
+
let outputs: t<'component, 'outputs, 'operations> => 'outputs
|
|
7
|
+
let wrappedOutputs: Pulumi.Output.t<t<'component, 'outputs, 'operations>> => Pulumi.Output.t<
|
|
8
|
+
'outputs,
|
|
9
|
+
>
|
|
10
|
+
let setOperations: (t<'component, 'outputs, 'operations>, Pulumi.Output.t<'operations>) => unit
|
|
11
|
+
let operations: t<'component, 'outputs, 'operations> => Pulumi.Output.t<'operations>
|
|
12
|
+
|
|
13
|
+
let toPulumiResource: t<'component, 'outputs, 'operations> => Pulumi.Resource.t
|
|
14
|
+
let fromPulumiResource: Pulumi.Resource.t => t<'component, 'outputs, 'operations>
|
|
15
|
+
|
|
16
|
+
let make: (
|
|
17
|
+
~componentType: string,
|
|
18
|
+
~name: string,
|
|
19
|
+
~construct: 'construct,
|
|
20
|
+
~opts: option<Pulumi.ComponentResource.options>,
|
|
21
|
+
) => t<'component, 'outputs, 'operations>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
A single counter increment item.
|
|
3
|
+
|
|
4
|
+
- `counterId` — which counter to increment
|
|
5
|
+
- `reference` — idempotency key (e.g. the triggering event's message ID)
|
|
6
|
+
- `inc` — how much to increment by (usually 1)
|
|
7
|
+
*/
|
|
8
|
+
type countItem = {
|
|
9
|
+
counterId: Reventless.Counter.counterId,
|
|
10
|
+
reference: Reventless.Counter.reference,
|
|
11
|
+
inc: int,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
A counter threshold target with an idempotency reference.
|
|
16
|
+
|
|
17
|
+
Used by `addToCounterTarget` to register that a target has been claimed by
|
|
18
|
+
a specific `reference`, preventing duplicate threshold triggers.
|
|
19
|
+
*/
|
|
20
|
+
type counterTargetRef = {
|
|
21
|
+
counterId: Reventless.Counter.counterId,
|
|
22
|
+
target: int,
|
|
23
|
+
targetRef: Reventless.Counter.reference,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
Deploy-time outputs produced when a `Counter` component is provisioned.
|
|
28
|
+
|
|
29
|
+
- `referencesDb` — tracks which references have already been counted (dedup)
|
|
30
|
+
- `countsDb` — stores the current count per `counterId`
|
|
31
|
+
*/
|
|
32
|
+
type outputs = {referencesDb: QueryDb.outputs, countsDb: QueryDb.outputs}
|
|
33
|
+
|
|
34
|
+
/** Increments one or more counters. Each item is applied idempotently via its `reference`. */
|
|
35
|
+
type count = array<countItem> => promise<unit>
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
Registers a threshold target, claiming it with a `targetRef`.
|
|
39
|
+
When the count reaches `target`, the event mapping's `AddToCounterTarget` action fires.
|
|
40
|
+
*/
|
|
41
|
+
type addToCounterTarget = counterTargetRef => promise<unit>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
Runtime operations exposed by a `Counter` component.
|
|
45
|
+
Available inside event mapping `map` functions via the injected `operations` record.
|
|
46
|
+
*/
|
|
47
|
+
type operations = {count: count, addToCounterTarget: addToCounterTarget}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
Processes a stream of raw event JSON values to extract and apply counter increments.
|
|
51
|
+
Used internally by the counter's Lambda handler.
|
|
52
|
+
*/
|
|
53
|
+
type jsonEventsHandler = Stream.t<JSON.t, string, unit> => Effect.t<unit, string, unit>
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
Module type for a `Counter` component.
|
|
57
|
+
|
|
58
|
+
The `Counter` component maintains named reference-counted thresholds.
|
|
59
|
+
When a count crosses a configured threshold, the associated event mapping
|
|
60
|
+
fires a command to the target aggregate.
|
|
61
|
+
*/
|
|
62
|
+
module type T = {
|
|
63
|
+
type component
|
|
64
|
+
let make: (
|
|
65
|
+
~name: string,
|
|
66
|
+
~jsonEventsHandler: jsonEventsHandler,
|
|
67
|
+
~ttl: int=?,
|
|
68
|
+
/** ESM specifier of the counter target's spec module. Used by bundling
|
|
69
|
+
adapters that load callbacks via `import()`; other adapters ignore it. */
|
|
70
|
+
~specModulePath: string=?,
|
|
71
|
+
/** ESM specifier of the counter target's mappings module. Used by bundling
|
|
72
|
+
adapters that load callbacks via `import()`; other adapters ignore it. */
|
|
73
|
+
~mappingsModulePath: string=?,
|
|
74
|
+
/** Publish-channel address of the aggregate this counter dispatches commands
|
|
75
|
+
to. Used by bundling adapters; other adapters ignore it. */
|
|
76
|
+
~publishChannelId: Pulumi.Output.t<string>=?,
|
|
77
|
+
~opts: Pulumi.ComponentResource.options=?,
|
|
78
|
+
) => component
|
|
79
|
+
let outputs: component => outputs
|
|
80
|
+
let operations: component => Pulumi.Output.t<operations>
|
|
81
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Deploy-time outputs produced when a `DcbEventLog` is provisioned.
|
|
3
|
+
|
|
4
|
+
- `resources` — the underlying storage infrastructure (e.g. DynamoDB table)
|
|
5
|
+
- `eventTopic` — the SNS topic for downstream subscribers (state view slices)
|
|
6
|
+
*/
|
|
7
|
+
type outputs = {resources: array<Adapter.resource>, eventTopic: EventTopic.outputs}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
A raw event ready to be stored in the DCB log.
|
|
11
|
+
Produced by slice callbacks after encoding with their eventSchema.
|
|
12
|
+
|
|
13
|
+
`meta` is the envelope metadata for this event (causation, correlation, tracing).
|
|
14
|
+
Slice callbacks derive it from the triggering message's context.
|
|
15
|
+
*/
|
|
16
|
+
type rawEvent = {
|
|
17
|
+
eventType: string,
|
|
18
|
+
data: JSON.t,
|
|
19
|
+
tags: array<Reventless.DcbTag.tag>,
|
|
20
|
+
meta: Reventless.Message.meta,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
A raw event read from the DCB log at a known position.
|
|
25
|
+
Consumed by slice callbacks for decoding with their consumedEventSchema.
|
|
26
|
+
|
|
27
|
+
`meta` is the envelope metadata that was written at append time.
|
|
28
|
+
`recordedAt` is the storage timestamp, set by the storage adapter at append.
|
|
29
|
+
*/
|
|
30
|
+
type rawSequencedEvent = {
|
|
31
|
+
position: Reventless.DcbTag.sequencePosition,
|
|
32
|
+
eventType: string,
|
|
33
|
+
data: JSON.t,
|
|
34
|
+
tags: array<Reventless.DcbTag.tag>,
|
|
35
|
+
meta: Reventless.Message.meta,
|
|
36
|
+
recordedAt: string,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
The result of a DCB `read` operation.
|
|
41
|
+
|
|
42
|
+
- `events` — the matching events in sequence order
|
|
43
|
+
- `headPosition` — the sequence position of the last event read (use as the
|
|
44
|
+
`after` cursor in a subsequent `appendCondition` to detect conflicts)
|
|
45
|
+
*/
|
|
46
|
+
type readResult = {
|
|
47
|
+
events: array<rawSequencedEvent>,
|
|
48
|
+
headPosition?: Reventless.DcbTag.sequencePosition,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
Reads events from the DCB log matching the given query.
|
|
53
|
+
|
|
54
|
+
- `~query` — content-based filter (event types + tags)
|
|
55
|
+
- `~after` — optional cursor; only events after this position are returned
|
|
56
|
+
*/
|
|
57
|
+
type read = (
|
|
58
|
+
~query: Reventless.DcbTag.query,
|
|
59
|
+
~after: Reventless.DcbTag.sequencePosition=?,
|
|
60
|
+
) => promise<readResult>
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
Appends raw events to the DCB log with optional optimistic-concurrency checking.
|
|
64
|
+
|
|
65
|
+
Returns `Ok(position)` on success or `Error(reason)` if the append condition
|
|
66
|
+
was violated (i.e. the log was modified since `condition.after`).
|
|
67
|
+
*/
|
|
68
|
+
type append = (
|
|
69
|
+
array<rawEvent>,
|
|
70
|
+
~condition: Reventless.DcbTag.appendCondition=?,
|
|
71
|
+
) => promise<result<Reventless.DcbTag.sequencePosition, string>>
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
Streams events from the DCB log matching the given query.
|
|
75
|
+
Use for large result sets that should not be loaded into memory at once.
|
|
76
|
+
*/
|
|
77
|
+
type readStream = (
|
|
78
|
+
~query: Reventless.DcbTag.query,
|
|
79
|
+
~after: Reventless.DcbTag.sequencePosition=?,
|
|
80
|
+
/** Opt into strongly-consistent single-tag reads. Defaults to eventually
|
|
81
|
+
consistent: a stale read can only cause a rejected append (then a retry),
|
|
82
|
+
never a wrong write, because the append fence is always evaluated strongly.
|
|
83
|
+
The slice callback sets this on retries (eventual-first, strong-on-retry). */
|
|
84
|
+
~strongConsistency: bool=?,
|
|
85
|
+
) => Stream.t<rawSequencedEvent, string, unit>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
Appends a stream of raw events to the DCB log as an `Effect.t`.
|
|
89
|
+
Use for high-throughput batch imports.
|
|
90
|
+
*/
|
|
91
|
+
type appendStream = (
|
|
92
|
+
Stream.t<rawEvent, string, unit>,
|
|
93
|
+
~condition: Reventless.DcbTag.appendCondition=?,
|
|
94
|
+
) => Effect.t<result<Reventless.DcbTag.sequencePosition, string>, string, unit>
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
Runtime operations exposed by a `DcbEventLog` component.
|
|
98
|
+
Works with raw events — encode/decode is handled by each slice's callback.
|
|
99
|
+
|
|
100
|
+
Obtained via `Component.operations(dcbEventLog)`. Available inside Lambda handlers.
|
|
101
|
+
*/
|
|
102
|
+
type operations = {
|
|
103
|
+
read: read,
|
|
104
|
+
append: append,
|
|
105
|
+
readStream: readStream,
|
|
106
|
+
appendStream: appendStream,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type t
|
|
110
|
+
type component = Component.t<t, outputs, operations>
|
|
111
|
+
|
|
112
|
+
module type T = {
|
|
113
|
+
type component = component
|
|
114
|
+
let make: (~name: string, ~indexes: array<string>=?, ~opts: Pulumi.ComponentResource.options=?) => component
|
|
115
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enqueues an event for processing by a downstream component (e.g. a read model).
|
|
3
|
+
*
|
|
4
|
+
* - `delay` — delivery delay in seconds (0 for immediate delivery)
|
|
5
|
+
* - `id` — the aggregate ID string the event belongs to
|
|
6
|
+
* - `message` — the serialized event payload (JSON string)
|
|
7
|
+
*
|
|
8
|
+
* This function is exposed by read model `operations` so that the aggregate
|
|
9
|
+
* runtime can push events without knowing the read model's infrastructure details.
|
|
10
|
+
*/
|
|
11
|
+
type enqueueEvent = (/* ~delay: */ int, /* ~id: */ string, /* ~message: */ string) => promise<unit>
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Deploy-time outputs produced when an `EventCollector` is provisioned.
|
|
15
|
+
*
|
|
16
|
+
* An `EventCollector` is the inbound queue of a read model — events are collected
|
|
17
|
+
* here and then processed in order by the read model's projection function.
|
|
18
|
+
*/
|
|
19
|
+
type outputs = {name: string, resources: array<Adapter.resource>}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Module type for an aggregate's event log specification.
|
|
3
|
+
|
|
4
|
+
`EventLog.T` is used by the framework to configure the event storage backend
|
|
5
|
+
(e.g. a DynamoDB table) and the associated event topic for downstream subscribers.
|
|
6
|
+
*/
|
|
7
|
+
module type T = {
|
|
8
|
+
module Id: Reventless.Id.T
|
|
9
|
+
|
|
10
|
+
/** Logical name of the aggregate / event stream (used as a table-name prefix). */
|
|
11
|
+
let name: string
|
|
12
|
+
|
|
13
|
+
/** The event type stored in this log. Must carry `@schema` for serialization. */
|
|
14
|
+
@schema
|
|
15
|
+
type event
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
Deploy-time outputs produced when an `EventLog` is provisioned.
|
|
20
|
+
|
|
21
|
+
- `resources` — the underlying infrastructure resources (e.g. DynamoDB table)
|
|
22
|
+
- `eventTopic` — the event topic outputs for downstream subscribers
|
|
23
|
+
*/
|
|
24
|
+
type outputs = {resources: array<Adapter.resource>, eventTopic: EventTopic.outputs}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Deploy-time outputs produced when an `EventMapper` is provisioned.
|
|
3
|
+
|
|
4
|
+
An `EventMapper` subscribes to an aggregate's event topic and routes events
|
|
5
|
+
to one or more target aggregate command topics, optionally via a `Counter`.
|
|
6
|
+
*/
|
|
7
|
+
type outputs = {
|
|
8
|
+
name: string,
|
|
9
|
+
eventCollector: Pulumi.Output.t<EventCollector.outputs>,
|
|
10
|
+
counter?: Counter.outputs,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
A collection of `EventMapping.T` modules that route events from one aggregate
|
|
15
|
+
to commands on one or more target aggregates.
|
|
16
|
+
|
|
17
|
+
Pass a `Mappings` module to `Platform.Aggregate.Make` as the third argument.
|
|
18
|
+
Use `NoEventMappings.Make(TargetSpec)` when no routing is needed.
|
|
19
|
+
|
|
20
|
+
@example
|
|
21
|
+
```rescript
|
|
22
|
+
// CatalogPlugin.res
|
|
23
|
+
module CategoryMappings: Mappings with module Target := CategoriesReadModel = {
|
|
24
|
+
module CategoryMappings = Mappings.Make(CategoriesReadModel)
|
|
25
|
+
module type Mapping = CategoryMappings.Mapping
|
|
26
|
+
let mappings = CategoriesProjections.mappings
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
*/
|
|
30
|
+
module type Mappings = {
|
|
31
|
+
module Target: Reventless.EventMapping.Target
|
|
32
|
+
module type Mapping = Reventless.EventMapping.T with module Target := Target
|
|
33
|
+
let moduleUrl: string
|
|
34
|
+
let mappings: array<module(Mapping)>
|
|
35
|
+
/** Optional counter component for threshold-based command triggers. */
|
|
36
|
+
let counter: option<module(Counter.T)>
|
|
37
|
+
}
|