@reventlessdev/reventless-local 3.0.0-alpha.201 → 3.0.0-alpha.202
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.202 (2026-08-07)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **core,aws:** add the EventLogProvisioning seam for out-of-tree consumers ([82477b3](https://github.com/ReventlessDev/reventless-core/commit/82477b38c1ef90fbe3a6f4a114a9798770329470))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.201 (2026-08-05)
|
|
7
14
|
|
|
8
15
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-local",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.202",
|
|
4
4
|
"description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -35,18 +35,18 @@
|
|
|
35
35
|
"sury": "11.0.0-alpha.4",
|
|
36
36
|
"ws": "^8.18.0",
|
|
37
37
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
38
|
+
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.26",
|
|
38
39
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
39
40
|
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
|
|
40
|
-
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.26",
|
|
41
41
|
"@reventlessdev/rescript-node": "2.0.0-alpha.2",
|
|
42
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.214",
|
|
42
43
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
43
|
-
"@reventlessdev/reventless-
|
|
44
|
-
"@reventlessdev/reventless-
|
|
45
|
-
"@reventlessdev/reventless-
|
|
44
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.62",
|
|
45
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.161",
|
|
46
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.78",
|
|
46
47
|
"@reventlessdev/reventless-seed": "1.0.0-alpha.9",
|
|
47
|
-
"@reventlessdev/reventless-gwt": "1.0.0-alpha.160",
|
|
48
48
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.101",
|
|
49
|
-
"@reventlessdev/reventless-
|
|
49
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.127"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"rescript": "12.3.0",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// End-to-end check that the EventLogProvisioning seam fires from the real
|
|
2
|
+
// builders, not just from its own registry: a backend registered before the
|
|
3
|
+
// build sees both log styles as they are provisioned, through the local
|
|
4
|
+
// in-memory storage adapters.
|
|
5
|
+
//
|
|
6
|
+
// Registration and construction happen at module load — the seam fires during
|
|
7
|
+
// `Component.make`'s construct, so a backend registered inside a `test` body
|
|
8
|
+
// would arrive too late. The assertions then read what was recorded.
|
|
9
|
+
|
|
10
|
+
open JestGlobals
|
|
11
|
+
|
|
12
|
+
type record = {
|
|
13
|
+
logStyle: ReventlessCore.EventLogProvisioning.logStyle,
|
|
14
|
+
name: string,
|
|
15
|
+
owner: option<ReventlessCore.ResourceAttribution.owner>,
|
|
16
|
+
plugin: option<string>,
|
|
17
|
+
resourceCount: int,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let provisioned: array<record> = []
|
|
21
|
+
|
|
22
|
+
module Recorder: ReventlessCore.EventLogProvisioning.Backend = {
|
|
23
|
+
let onProvisioned = (~logStyle, ~name, ~owner, ~plugin, ~platform as _, ~resources, ~opts as _) =>
|
|
24
|
+
provisioned->Array.push({
|
|
25
|
+
logStyle,
|
|
26
|
+
name,
|
|
27
|
+
owner,
|
|
28
|
+
plugin,
|
|
29
|
+
resourceCount: resources->Array.length,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ReventlessCore.EventLogProvisioning.use(module(Recorder: ReventlessCore.EventLogProvisioning.Backend))
|
|
34
|
+
|
|
35
|
+
module Bus = LocalBus.Make()
|
|
36
|
+
|
|
37
|
+
let _ = TestRunner.setup()
|
|
38
|
+
|
|
39
|
+
// ── Classic log ──────────────────────────────────────────────
|
|
40
|
+
module SeamItemSpec = {
|
|
41
|
+
module Id = Reventless.Id.StringPure
|
|
42
|
+
let name = "SeamItemEventLog"
|
|
43
|
+
|
|
44
|
+
@schema
|
|
45
|
+
type event = | SeamItemCreated({name: string})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module ClassicMaker = ReventlessCore.EventLog_Builder.Make(
|
|
49
|
+
SeamItemSpec,
|
|
50
|
+
EventLogStorage_InMemory,
|
|
51
|
+
LocalEventTopicPublisher.Make(Bus),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// The plugin builder publishes this context around a plugin's construct; entering
|
|
55
|
+
// it here is what a real plugin build does, so the seam should carry the names.
|
|
56
|
+
let previousContext = ReventlessCore.ResourceAttribution.enter(~platform="seam-platform", ~plugin="SeamPlugin")
|
|
57
|
+
|
|
58
|
+
let classicLog = ClassicMaker.make(
|
|
59
|
+
~name="SeamItem",
|
|
60
|
+
~owner={kind: ReventlessCore.ComponentType.Aggregate, name: "SeamItem"},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
// ── DCB log ──────────────────────────────────────────────────
|
|
64
|
+
module DcbMaker = DcbEventLog_Builder.Make(Bus)
|
|
65
|
+
|
|
66
|
+
let dcbLog = DcbMaker.make(
|
|
67
|
+
~name="SeamCatalog",
|
|
68
|
+
~partitionTag=Reventless.DcbTag.Simple({key: "id"}),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
ReventlessCore.ResourceAttribution.restore(previousContext)
|
|
72
|
+
|
|
73
|
+
describe("EventLogProvisioning seam, driven by the real builders", () => {
|
|
74
|
+
testSync("fires once per log, for both styles", () => {
|
|
75
|
+
expect(provisioned->Array.map(r => (r.logStyle, r.name)))->toEqual([
|
|
76
|
+
(ReventlessCore.EventLogProvisioning.Classic, "SeamItemEventLog"),
|
|
77
|
+
(ReventlessCore.EventLogProvisioning.Dcb, "SeamCatalogDcbEventLog"),
|
|
78
|
+
])
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
testSync("carries the owning element: the aggregate for classic, the plugin for DCB", () => {
|
|
82
|
+
expect(provisioned->Array.map(r => r.owner))->toEqual([
|
|
83
|
+
Some(({kind: ReventlessCore.ComponentType.Aggregate, name: "SeamItem"}: ReventlessCore.ResourceAttribution.owner)),
|
|
84
|
+
Some(({kind: ReventlessCore.ComponentType.Plugin, name: "SeamCatalog"}: ReventlessCore.ResourceAttribution.owner)),
|
|
85
|
+
])
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
testSync("carries the ambient plugin attribution the builder published", () => {
|
|
89
|
+
expect(provisioned->Array.map(r => r.plugin))->toEqual([
|
|
90
|
+
Some("SeamPlugin"),
|
|
91
|
+
Some("SeamPlugin"),
|
|
92
|
+
])
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
testSync("carries the adapter's resources — empty for in-memory storage", () => {
|
|
96
|
+
// In-memory logs have nothing attachable; the seam still fires, and a backend
|
|
97
|
+
// that only implements a provider arm ignores them.
|
|
98
|
+
expect(provisioned->Array.map(r => r.resourceCount))->toEqual([0, 0])
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test("the components still build normally with a backend registered", async () => {
|
|
102
|
+
let classicOps = await classicLog->ReventlessCore.Component.operations->TestRunner.resolve
|
|
103
|
+
let dcbOps = await dcbLog->DcbMaker.operations->TestRunner.resolve
|
|
104
|
+
// Resolving both operation records proves construction completed with a
|
|
105
|
+
// backend registered — the seam fires mid-construct, so a throwing backend
|
|
106
|
+
// would take the build down here.
|
|
107
|
+
expect(classicOps)->toBeTruthy
|
|
108
|
+
expect(dcbOps)->toBeTruthy
|
|
109
|
+
})
|
|
110
|
+
})
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
|
+
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
6
|
+
import * as LocalBus$ReventlessLocal from "../../../src/adapter/LocalBus.res.mjs";
|
|
7
|
+
import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
|
|
8
|
+
import * as EventLog_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventLog/EventLog_Builder.res.mjs";
|
|
9
|
+
import * as ResourceAttribution$ReventlessCore from "@reventlessdev/reventless-core/src/ResourceAttribution.res.mjs";
|
|
10
|
+
import * as DcbEventLog_Builder$ReventlessLocal from "../../../src/components/DcbEventLog_Builder.res.mjs";
|
|
11
|
+
import * as EventLogProvisioning$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/EventLogProvisioning/EventLogProvisioning.res.mjs";
|
|
12
|
+
import * as EventLogStorage_InMemory$ReventlessLocal from "../../../src/adapter/EventLog/EventLogStorage_InMemory.res.mjs";
|
|
13
|
+
import * as LocalEventTopicPublisher$ReventlessLocal from "../../../src/adapter/EventTopic/LocalEventTopicPublisher.res.mjs";
|
|
14
|
+
|
|
15
|
+
let provisioned = [];
|
|
16
|
+
|
|
17
|
+
function onProvisioned(logStyle, name, owner, plugin, param, resources, param$1) {
|
|
18
|
+
provisioned.push({
|
|
19
|
+
logStyle: logStyle,
|
|
20
|
+
name: name,
|
|
21
|
+
owner: owner,
|
|
22
|
+
plugin: plugin,
|
|
23
|
+
resourceCount: resources.length
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let Recorder = {
|
|
28
|
+
onProvisioned: onProvisioned
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
EventLogProvisioning$ReventlessCore.use(Recorder);
|
|
32
|
+
|
|
33
|
+
let Bus = LocalBus$ReventlessLocal.Make({});
|
|
34
|
+
|
|
35
|
+
TestRunner$ReventlessLocal.setup();
|
|
36
|
+
|
|
37
|
+
let name = "SeamItemEventLog";
|
|
38
|
+
|
|
39
|
+
let eventSchema = S.schema(s => ({
|
|
40
|
+
TAG: "SeamItemCreated",
|
|
41
|
+
name: s.m(S.string)
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
let SeamItemSpec = {
|
|
45
|
+
Id: undefined,
|
|
46
|
+
name: name,
|
|
47
|
+
eventSchema: eventSchema
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
let ClassicMaker = EventLog_Builder$ReventlessCore.Make({
|
|
51
|
+
Id: {
|
|
52
|
+
schema: Id$Reventless.StringPure.schema,
|
|
53
|
+
make: prim => prim,
|
|
54
|
+
makeFromString: prim => prim,
|
|
55
|
+
toString: prim => prim,
|
|
56
|
+
cmp: Id$Reventless.StringPure.cmp
|
|
57
|
+
},
|
|
58
|
+
name: name,
|
|
59
|
+
eventSchema: eventSchema
|
|
60
|
+
})({
|
|
61
|
+
make: EventLogStorage_InMemory$ReventlessLocal.make
|
|
62
|
+
})(LocalEventTopicPublisher$ReventlessLocal.Make(Bus));
|
|
63
|
+
|
|
64
|
+
let previousContext = ResourceAttribution$ReventlessCore.enter("seam-platform", "SeamPlugin");
|
|
65
|
+
|
|
66
|
+
let classicLog = ClassicMaker.make("SeamItem", {
|
|
67
|
+
kind: "Aggregate",
|
|
68
|
+
name: "SeamItem"
|
|
69
|
+
}, undefined);
|
|
70
|
+
|
|
71
|
+
let DcbMaker = DcbEventLog_Builder$ReventlessLocal.Make(Bus);
|
|
72
|
+
|
|
73
|
+
let dcbLog = DcbMaker.make("SeamCatalog", undefined, {
|
|
74
|
+
TAG: "Simple",
|
|
75
|
+
_0: {
|
|
76
|
+
key: "id"
|
|
77
|
+
}
|
|
78
|
+
}, undefined);
|
|
79
|
+
|
|
80
|
+
ResourceAttribution$ReventlessCore.restore(previousContext);
|
|
81
|
+
|
|
82
|
+
globalThis.describe("EventLogProvisioning seam, driven by the real builders", () => {
|
|
83
|
+
globalThis.test("fires once per log, for both styles", () => {
|
|
84
|
+
globalThis.expect(provisioned.map(r => [
|
|
85
|
+
r.logStyle,
|
|
86
|
+
r.name
|
|
87
|
+
])).toEqual([
|
|
88
|
+
[
|
|
89
|
+
"Classic",
|
|
90
|
+
"SeamItemEventLog"
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
"Dcb",
|
|
94
|
+
"SeamCatalogDcbEventLog"
|
|
95
|
+
]
|
|
96
|
+
]);
|
|
97
|
+
});
|
|
98
|
+
globalThis.test("carries the owning element: the aggregate for classic, the plugin for DCB", () => {
|
|
99
|
+
globalThis.expect(provisioned.map(r => r.owner)).toEqual([
|
|
100
|
+
{
|
|
101
|
+
kind: "Aggregate",
|
|
102
|
+
name: "SeamItem"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
kind: "Plugin",
|
|
106
|
+
name: "SeamCatalog"
|
|
107
|
+
}
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
globalThis.test("carries the ambient plugin attribution the builder published", () => {
|
|
111
|
+
globalThis.expect(provisioned.map(r => r.plugin)).toEqual([
|
|
112
|
+
"SeamPlugin",
|
|
113
|
+
"SeamPlugin"
|
|
114
|
+
]);
|
|
115
|
+
});
|
|
116
|
+
globalThis.test("carries the adapter's resources — empty for in-memory storage", () => {
|
|
117
|
+
globalThis.expect(provisioned.map(r => r.resourceCount)).toEqual([
|
|
118
|
+
0,
|
|
119
|
+
0
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
globalThis.test("the components still build normally with a backend registered", async () => {
|
|
123
|
+
let classicOps = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(classicLog));
|
|
124
|
+
let dcbOps = await TestRunner$ReventlessLocal.resolve(DcbMaker.operations(dcbLog));
|
|
125
|
+
globalThis.expect(classicOps).toBeTruthy();
|
|
126
|
+
globalThis.expect(dcbOps).toBeTruthy();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
provisioned,
|
|
132
|
+
Recorder,
|
|
133
|
+
Bus,
|
|
134
|
+
SeamItemSpec,
|
|
135
|
+
ClassicMaker,
|
|
136
|
+
previousContext,
|
|
137
|
+
classicLog,
|
|
138
|
+
DcbMaker,
|
|
139
|
+
dcbLog,
|
|
140
|
+
}
|
|
141
|
+
/* Not a pure module */
|