@reventlessdev/reventless-core 3.0.0-alpha.155 → 3.0.0-alpha.157
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 +23 -0
- package/README.md +49 -150
- package/package.json +13 -13
- package/src/admin/AdminApi.res +80 -10
- package/src/admin/AdminApi.res.mjs +45 -7
- package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry.res +65 -0
- package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry.res.mjs +87 -0
- package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry_Behavior.res +58 -0
- package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry_Behavior.res.mjs +135 -0
- package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments.res +44 -0
- package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments.res.mjs +74 -0
- package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments_Projection.res +50 -0
- package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments_Projection.res.mjs +80 -0
- package/src/admin/Platform_Admin.res +37 -1
- package/src/admin/Platform_Admin.res.mjs +18 -5
- package/src/admin/Platform_ApiFragmentsApi.res +32 -0
- package/src/admin/Platform_ApiFragmentsApi.res.mjs +52 -0
- package/src/admin/Platform_UIFragmentsApi.res +1 -1
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res +50 -0
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res.mjs +70 -0
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry_Behavior.res +33 -0
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry_Behavior.res.mjs +80 -0
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments.res +33 -0
- package/src/admin/{UIFragmentRegistryReadModelSpec.res.mjs → UiFragmentRegistry/StateViewSlice/UiFragments.res.mjs} +24 -2
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res +34 -0
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res.mjs +54 -0
- package/src/components/Api/Api_Naming.res +1 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res +7 -11
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +8 -22
- package/src/components/Api/GraphQL_PushPlanner.res +65 -0
- package/src/components/Api/GraphQL_PushPlanner.res.mjs +46 -0
- package/src/components/Api/GraphQL_Stitcher.res +72 -1
- package/src/components/Api/GraphQL_Stitcher.res.mjs +53 -2
- package/src/components/Dcb/Dcb_Builder.res +16 -6
- package/src/components/Dcb/Dcb_Builder.res.mjs +10 -7
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +6 -1
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +1 -1
- package/src/plugin/api/PluginBaseFragment.res +5 -6
- package/src/plugin/api/PluginBaseFragment.res.mjs +0 -1
- package/src/plugin/component/Plugin_Builder.res +4 -1
- package/src/plugin/component/Plugin_Builder.res.mjs +7 -6
- package/src/plugin/component/Plugin_Helpers.res +30 -0
- package/src/plugin/component/Plugin_Helpers.res.mjs +13 -3
- package/src/plugin/component/Plugin_SubscriptionSchema.res +19 -9
- package/src/plugin/component/Plugin_SubscriptionSchema.res.mjs +23 -14
- package/src/plugin/connect/PluginConnectExtension_Mapping.res +28 -6
- package/src/plugin/connect/PluginConnectExtension_Mapping.res.mjs +34 -14
- package/src/plugin/connect/PluginExtensionPoint_Builder.res +7 -1
- package/src/plugin/connect/PluginExtensionPoint_Builder.res.mjs +5 -1
- package/src/plugin/connect/PluginExtensionPoint_Plugin.res +3 -1
- package/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs +4 -2
- package/src/plugin/connect/PluginExtensionPoint_UiFragment.res +52 -0
- package/src/plugin/connect/PluginExtensionPoint_UiFragment.res.mjs +74 -0
- package/src/plugin/lifecycle/PluginBehavior.res +9 -51
- package/src/plugin/lifecycle/PluginBehavior.res.mjs +39 -68
- package/src/plugin/lifecycle/PluginSpec.res +0 -18
- package/src/plugin/lifecycle/PluginSpec.res.mjs +0 -30
- package/src/plugin/lifecycle/PluginsProjection.res +2 -6
- package/src/plugin/lifecycle/PluginsProjection.res.mjs +0 -2
- package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
- package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +0 -1
- package/tests/admin/Platform_ApiFragmentsApiTest.res +66 -0
- package/tests/admin/Platform_ApiFragmentsApiTest.res.mjs +68 -0
- package/tests/admin/Platform_UIFragmentsApiTest.res +1 -1
- package/tests/api/GraphQL_PushPlannerTest.res +62 -0
- package/tests/api/GraphQL_PushPlannerTest.res.mjs +80 -0
- package/tests/api/GraphQL_StitcherTest.res +63 -0
- package/tests/api/GraphQL_StitcherTest.res.mjs +75 -0
- package/tests/logger/LogFormatTest.res +1 -2
- package/tests/logger/LogFormatTest.res.mjs +1 -2
- package/tests/message/MessageTest.res +0 -3
- package/tests/message/MessageTest.res.mjs +0 -3
- package/src/admin/UIFragmentRegistryProjection.res +0 -43
- package/src/admin/UIFragmentRegistryProjection.res.mjs +0 -81
- package/src/admin/UIFragmentRegistryReadModelSpec.res +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
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.157 (2026-07-12)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **admin:** event-source the API fragment registry as admin DCB slices (Phase 2b) ([3fc40d2](https://github.com/ReventlessDev/reventless-core/commit/3fc40d247c54bf66ed84d625959f08299aa04a98))
|
|
11
|
+
* **admin:** event-source the UI fragment registry as admin DCB slices ([711581e](https://github.com/ReventlessDev/reventless-core/commit/711581e77c626e7d0fc35db8ec351f62a70bd8f2))
|
|
12
|
+
* **admin:** expose the API fragment registry as system-callable mutations (Phase 2d) ([8b1ec49](https://github.com/ReventlessDev/reventless-core/commit/8b1ec49813e67f303bc86d60470c9b95d2a8408e))
|
|
13
|
+
* **admin:** make the API fragment registry per-target (Domain | Platform) ([30491a9](https://github.com/ReventlessDev/reventless-core/commit/30491a9e14b4236c98cc756efb6de68ede1e77d7))
|
|
14
|
+
* **admin:** populate the API fragment registry on connect + status query (Phase 2c) ([106d96c](https://github.com/ReventlessDev/reventless-core/commit/106d96c904e135f8360d53e582f395c107db704a))
|
|
15
|
+
* **admin:** react to ApiFragment* events with a schema single writer (Phase 2e) ([59f9e46](https://github.com/ReventlessDev/reventless-core/commit/59f9e46c154d8ca2a34537974722014fc279de3e))
|
|
16
|
+
* **admin:** retire the Plugin-aggregate UI-fragment path in favour of the registry slices ([1dbc708](https://github.com/ReventlessDev/reventless-core/commit/1dbc708e7439b34ff970cc3d963d7835a8c6fd48))
|
|
17
|
+
* **admin:** serve Platform_UIFragments from the new UiFragmentRegistry slices ([7a47533](https://github.com/ReventlessDev/reventless-core/commit/7a47533acc95977fbeeb6375e94449056b655abc))
|
|
18
|
+
* **api:** neutralize core-emitted SDL — [@aws](https://github.com/aws)_subscribe moves to the AWS adapter (Phase 2a) ([9b27ec8](https://github.com/ReventlessDev/reventless-core/commit/9b27ec8f6ab2b7453b1c50b9891e89781ec3b31a))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# 3.0.0-alpha.156 (2026-07-11)
|
|
22
|
+
|
|
23
|
+
**Note:** Version bump only for package @reventlessdev/reventless-core
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
6
29
|
# 3.0.0-alpha.155 (2026-07-11)
|
|
7
30
|
|
|
8
31
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -1,173 +1,72 @@
|
|
|
1
|
-
[](./CHANGELOG.md)
|
|
1
|
+
[](https://www.npmjs.com/package/@reventlessdev/reventless-core)
|
|
2
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
3
|
+
[](https://docs.reventless.dev)
|
|
5
4
|
|
|
6
|
-
#
|
|
5
|
+
# @reventlessdev/reventless-core
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
> ⚠️ **Alpha.** APIs and on-disk formats can change without notice between releases.
|
|
8
|
+
> Pin exact versions and expect breaking changes.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
The **provider-agnostic core** of [Reventless](https://docs.reventless.dev) — a
|
|
11
|
+
spec-driven, event-sourced CQRS framework written in [ReScript](https://rescript-lang.org).
|
|
12
|
+
This package is the runtime you model your domain against: the type-safe building blocks
|
|
13
|
+
for commands, events, projections, and the behaviors that evolve state — with **no** cloud
|
|
14
|
+
or storage dependency. The provider-specific adapters live in separate packages
|
|
15
|
+
(see [Where it fits](#where-it-fits)).
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
* [ReasonML](https://reasonml.github.io/) using [Bucklescript](https://bucklescript.github.io/)
|
|
14
|
-
* Event-Sourced
|
|
15
|
-
* CQRS
|
|
17
|
+
## What it provides
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
### 1. Install Node 12.x (LTS)
|
|
19
|
-
We use `fnm` to manage the locally installed node-version.
|
|
20
|
-
The currently used/tested node version is stated in the `.node-version` file.
|
|
19
|
+
ReScript modules, consumed by adding the package to your `rescript.json` `dependencies`:
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
- **`Message`** — command / event message contracts and their service interface.
|
|
22
|
+
- **`Behavior`** — the write-side behavior spec: event-sourced state transitions
|
|
23
|
+
(`evolve(state, event) => state`).
|
|
24
|
+
- **`Projection`** / **`ProjectionMapper`** — read models projected from event streams.
|
|
25
|
+
- **`Mapper`** / **`Mapper1toN`** / **`MapperNto1`** — cross-component event translation
|
|
26
|
+
(encode/decode between a source and a target contract).
|
|
27
|
+
- **`ComponentType`** — the hierarchical component-kind vocabulary.
|
|
28
|
+
- **`PluginRuntimeOperations`** — the provider-agnostic interface a runtime adapter implements.
|
|
29
|
+
- **`RequestContext`** — per-invocation context threaded through the effect pipeline.
|
|
30
|
+
- **`Env`** — environment access.
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
Domain code written against these modules is provider-agnostic: the same components run
|
|
33
|
+
unchanged on any adapter.
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
## Where it fits
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
`reventless-core` is one package in the Reventless framework. It builds on
|
|
38
|
+
[`@reventlessdev/reventless-spec`](https://www.npmjs.com/package/@reventlessdev/reventless-spec)
|
|
39
|
+
(type specifications) and is paired at runtime with a storage/cloud adapter:
|
|
31
40
|
|
|
32
|
-
[
|
|
33
|
-
[
|
|
41
|
+
- [`@reventlessdev/reventless-aws`](https://www.npmjs.com/package/@reventlessdev/reventless-aws) — AWS (DynamoDB, Lambda, SQS, SNS, S3)
|
|
42
|
+
- [`@reventlessdev/reventless-postgres`](https://www.npmjs.com/package/@reventlessdev/reventless-postgres) — Postgres event log + query DB
|
|
43
|
+
- [`@reventlessdev/reventless-local`](https://www.npmjs.com/package/@reventlessdev/reventless-local) — in-memory / SQLite platform for local dev and tests
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
npm install bs-platform@5.2.1 --no-save
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### 3. Install Dependencies
|
|
41
|
-
```
|
|
42
|
-
npm install
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 3. Build Project
|
|
46
|
-
```
|
|
47
|
-
npm run build
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Or in development, to run a watcher:
|
|
51
|
-
```
|
|
52
|
-
npm run start
|
|
53
|
-
```
|
|
45
|
+
You normally obtain `reventless-core` transitively by scaffolding an app rather than
|
|
46
|
+
installing it on its own.
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
Otherwise, see https://reasonml.github.io/docs/en/editor-plugins.
|
|
48
|
+
## Install
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
### 4. Test Project
|
|
61
|
-
Run Test-Suite once:
|
|
62
|
-
```
|
|
63
|
-
npm run test
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Run Test-Suite continously on file changes:
|
|
67
|
-
```
|
|
68
|
-
npm run dev
|
|
50
|
+
```bash
|
|
51
|
+
pnpm add @reventlessdev/reventless-core
|
|
69
52
|
```
|
|
70
53
|
|
|
71
|
-
|
|
72
|
-
|
|
54
|
+
Then register it as a ReScript dependency in `rescript.json`:
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
// __tests__/try_test.re
|
|
79
|
-
open Jest
|
|
80
|
-
|
|
81
|
-
describe("Try should return...", () => {
|
|
82
|
-
open Expect
|
|
83
|
-
test("the same text given a count of 0", () => {
|
|
84
|
-
expect(Try.f("test", 0)) |> toBe("test")
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
test("the text doubled given a count of 1", () => {
|
|
88
|
-
expect(Try.f("test", 1)) |> toBe("testtest")
|
|
89
|
-
})
|
|
90
|
-
})
|
|
91
|
-
```
|
|
92
|
-
File Try:
|
|
93
|
-
```
|
|
94
|
-
// src/Try.re
|
|
95
|
-
let rec f = (text: string, count: int) => {
|
|
96
|
-
if(count <= 0) text
|
|
97
|
-
else f(text ++ text, count - 1)
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"dependencies": ["@reventlessdev/reventless-core"]
|
|
98
59
|
}
|
|
99
60
|
```
|
|
100
61
|
|
|
101
|
-
|
|
102
|
-
**TODO: This should be moved to a separate chapter on how to use Reventless in a project.**
|
|
103
|
-
|
|
104
|
-
Before running `npm run deploy`, make sure to update the `.env` file. (Copy your Pulumi Access Token from your profile preferences into the `.env` file.)
|
|
105
|
-
You also need to set the environment variable like `export AWS_SDK_LOAD_CONFIG=1` and hava a file at `~/.aws/config`:
|
|
106
|
-
```
|
|
107
|
-
[default]
|
|
108
|
-
region = eu-west-1
|
|
109
|
-
output = json
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
This command will run 3 Shell-Scripts:
|
|
113
|
-
* `./scripts/pre-deploy.sh`: Move everything in `./node_modules/bs-platform` to a tmp-directory, but the JS-lib
|
|
114
|
-
* `./scripts/pulumi-up.sh`: Read `PULUMI_ACCESS_TOKEN` and `PULUMI_STACK` from the `.env` file and run `pulumi up` to actually deploy (This script will be only executed, if `pre-deploy` exited successfully.)
|
|
115
|
-
* `./scripts/post-deploy.sh`: Move `bs-platform` back into place from tmp-directory.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
**NOTE**: The `./scripts/pulumi-up.sh` exports the environment variable `PULUMI_ACCESS_TOKEN` to make it to the Pulumi CLI available. Export another value and set a `#` before the apropriate line inside`.env` to use another token.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
## Setup environment for local development of actual project and framework side by side -- DEPRECATED
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
**🚨 We encountered some deployment issues, when using npm link. Therefore we discourage using this technic for the time being❗**
|
|
125
|
-
|
|
126
|
-
### Setup the new project
|
|
127
|
-
* add a new "deploy key" in [gitlab](https://github.com/atos-austria/reason/reventless/settings/repository/deploy_token/create#js-deploy-tokens)
|
|
128
|
-
* add "private repository" with the new deploy-token to the new project: `"reventless": "git+https://USER-TOKEN:PASSWORD-TOKEN@github.com/atos-austria/reason/reventless.git"`
|
|
129
|
-
* run `npm install` for the new project
|
|
130
|
-
|
|
131
|
-
### Setup the framework project to use the local version in the new version
|
|
132
|
-
* clone the framework repo into a local directory
|
|
133
|
-
* run `npm link` inside the framework's directory
|
|
134
|
-
* run `npm link reventless` inside the new project's directory
|
|
135
|
-
|
|
136
|
-
## Go back to using the actual framwork-repo as dependency
|
|
137
|
-
### Unlink
|
|
138
|
-
[Medium Post](https://medium.com/@alexishevia/the-magic-behind-npm-link-d94dcb3a81af)
|
|
139
|
-
|
|
140
|
-
* run `npm unlink --no-save reventless` in the new project's directory
|
|
141
|
-
* run `npm unlink` in the framework's directory
|
|
142
|
-
|
|
143
|
-
### Alternative
|
|
144
|
-
[Medium Post](https://medium.com/dailyjs/how-to-use-npm-link-7375b6219557)
|
|
145
|
-
|
|
146
|
-
* run `npm uninstall --no-save reventless && npm install` inside the new project's directory
|
|
147
|
-
* OPTIONAL: to delete the global symlink of the framework run `npm uninstall` inside the framework's directory
|
|
148
|
-
|
|
149
|
-
## Ressources
|
|
150
|
-
* [Project Wiki](https://github.com/atos-austria/reason/reventless/wikis/home)
|
|
151
|
-
|
|
152
|
-
# Coding Guidelines
|
|
153
|
-
|
|
154
|
-
* `<Component>.Make.createComponent` (binding to `Component.js` constructor): Pass any additional parameters (besides `componentType`, `name`, `opts`) directly to the `construct` function (using partial application), like this: `createComponent(~componentType, ~name, ~construct=construct(~param1, ~param2), ~opts)`
|
|
155
|
-
|
|
156
|
-
# Code-Smells
|
|
157
|
-
|
|
158
|
-
## ReasonML
|
|
159
|
-
|
|
160
|
-
* `...->ignore`
|
|
161
|
-
|
|
162
|
-
## Pulumi
|
|
62
|
+
Requires ReScript `^12.3.0` (peer dependency).
|
|
163
63
|
|
|
164
|
-
|
|
165
|
-
* `...->Pulumi.Output.all->Pulumi.Output.apply(...)`
|
|
166
|
-
* type `option(Pulumi.Output.t('a))`: Rescript's js option encoding and Pulumi's Output lifting mechanics interact badly! - Therefore, such a type is not possible to use!
|
|
64
|
+
## Links
|
|
167
65
|
|
|
168
|
-
|
|
66
|
+
- 📚 Documentation — [docs.reventless.dev](https://docs.reventless.dev)
|
|
67
|
+
- 📦 Repository — [ReventlessDev/reventless-core](https://github.com/ReventlessDev/reventless-core)
|
|
68
|
+
- 📋 [Changelog](./CHANGELOG.md)
|
|
169
69
|
|
|
170
|
-
##
|
|
70
|
+
## License
|
|
171
71
|
|
|
172
|
-
|
|
173
|
-
To fix this use desired ranges in the dependencies definition (package.json) of the project and check upstream version ranges as well. (You can also use `npm why <packageName>` to see where a specific version is coming from.)
|
|
72
|
+
[Apache-2.0](https://opensource.org/licenses/Apache-2.0)
|
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.157",
|
|
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-alpha.4",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.
|
|
32
|
-
"@reventlessdev/rescript-
|
|
33
|
-
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.
|
|
34
|
-
"@reventlessdev/rescript-
|
|
35
|
-
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.
|
|
36
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.
|
|
37
|
-
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.
|
|
38
|
-
"@reventlessdev/
|
|
39
|
-
"@reventlessdev/
|
|
40
|
-
"@reventlessdev/reventless-interop": "3.0.0-alpha.
|
|
41
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
31
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.27",
|
|
32
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.7",
|
|
33
|
+
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.11",
|
|
34
|
+
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.12",
|
|
35
|
+
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.11",
|
|
36
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.15",
|
|
37
|
+
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.12",
|
|
38
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.98",
|
|
39
|
+
"@reventlessdev/rescript-uuid": "1.1.0-alpha.15",
|
|
40
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.26",
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.76"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "^12.3.0",
|
|
45
45
|
"sury-ppx": "11.0.0-alpha.2",
|
|
46
|
-
"@reventlessdev/reventless-ppx": "1.0.0-alpha.
|
|
46
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.53"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"rescript": "^12.3.0"
|
package/src/admin/AdminApi.res
CHANGED
|
@@ -25,9 +25,55 @@ let mutationEntries = (~cloner: bool) => {
|
|
|
25
25
|
|
|
26
26
|
let queryEntries = PluginBaseFragment.queryEntries
|
|
27
27
|
|
|
28
|
+
// ApiFragmentRegistry admin mutations — the real GraphQL surface of the ApiFragmentRegistry
|
|
29
|
+
// StateChangeSlice: the plugin / standalone-service deploy calls `Platform_RegisterApiFragment` /
|
|
30
|
+
// `Platform_DeregisterApiFragment` as a SigV4 system caller. Field names + arg SDL + the nested
|
|
31
|
+
// `fragment` input type are derived from the slice's `commandSchema` via the SAME
|
|
32
|
+
// `Api_Naming.sliceMutationFields` (admin "Platform" prefix) + `GraphQL_FragmentGenerator` path
|
|
33
|
+
// that the DCB builder uses, so the field declared here in the STATIC admin base is byte-identical
|
|
34
|
+
// to the one the DCB resolver binds to (`RecordApiFragmentPush` is `@noApi`, filtered out).
|
|
35
|
+
//
|
|
36
|
+
// These live in `baseFragment` ONLY, never in the shared `mutationEntries`: only the static base is
|
|
37
|
+
// pushed to AppSync (`Platform.res` preAdminResolversSchemaHook), whereas the constructed admin
|
|
38
|
+
// fragment (`Platform_Admin.construct`) already carries them via `dcbResult.mutationEntries`. Adding
|
|
39
|
+
// them to `mutationEntries` — which feeds both — would duplicate the fields in the constructed
|
|
40
|
+
// fragment. They are folded into `baseFragment`'s own `generate` call (not a separate one) so its
|
|
41
|
+
// shared `seenTypes` dedups the shared `CommandResult` type family instead of re-emitting it.
|
|
42
|
+
let apiFragmentRegistryMutationEntries: array<mutationSchemaEntry> = {
|
|
43
|
+
let commandSchema = ApiFragmentRegistry.commandSchema->S.castToUnknown
|
|
44
|
+
let fieldSpecs = Api_Naming.sliceMutationFields(
|
|
45
|
+
~plugin="Platform",
|
|
46
|
+
~slice=ApiFragmentRegistry.name,
|
|
47
|
+
~commandSchema,
|
|
48
|
+
)
|
|
49
|
+
if fieldSpecs->Array.length === 0 {
|
|
50
|
+
[]
|
|
51
|
+
} else {
|
|
52
|
+
[
|
|
53
|
+
{
|
|
54
|
+
ReventlessInfra.Api.fieldNames: fieldSpecs->Array.map(((f, _)) => f),
|
|
55
|
+
commandSchema,
|
|
56
|
+
},
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Admin fields a deploy-time system caller (machine credentials — SigV4/IAM on AWS) invokes: the
|
|
62
|
+
// two ApiFragmentRegistry mutations (the deploy's register/deregister write path) and
|
|
63
|
+
// `Platform_ApiFragments` (the deploy waiter's push-status poll). Provider-neutral by design — the
|
|
64
|
+
// AWS adapter threads these into `injectAwsAuthAll`'s `~iamFieldNames` so only these fields carry
|
|
65
|
+
// the dual-auth (`@aws_cognito_user_pools @aws_iam`) directive; every other admin field stays
|
|
66
|
+
// Cognito-only. Derived from the same mutation entries so the names never drift from the SDL.
|
|
67
|
+
let systemCallerFieldNames =
|
|
68
|
+
apiFragmentRegistryMutationEntries
|
|
69
|
+
->Array.flatMap(e => e.fieldNames)
|
|
70
|
+
->Array.concat([Platform_ApiFragmentsApi.queryFieldName])
|
|
71
|
+
|
|
28
72
|
// UIFragment lifecycle Source C subscriptions.
|
|
29
|
-
// Mutations are called by the backend when UIFragment events occur;
|
|
30
|
-
//
|
|
73
|
+
// Mutations are called by the backend when UIFragment events occur; the
|
|
74
|
+
// subscription→mutation fan-in is carried as neutral `subscriptionSource`
|
|
75
|
+
// metadata (providers add their own routing — AppSync via `@aws_subscribe`
|
|
76
|
+
// appended at push time, the local platform via its PubSub bridge).
|
|
31
77
|
// manifest is serialised as a JSON string (String scalar, not AWSJSON).
|
|
32
78
|
|
|
33
79
|
let uiFragmentSubscriptionTypes = [
|
|
@@ -41,12 +87,21 @@ let uiFragmentMutationFields = [
|
|
|
41
87
|
` Platform_UIFragmentDeregistered(pluginId: ID!): UIFragmentChangeEvent`,
|
|
42
88
|
]
|
|
43
89
|
|
|
44
|
-
let uiFragmentSubscriptionField = ` onUIFragmentChange: UIFragmentChangeEvent
|
|
90
|
+
let uiFragmentSubscriptionField = ` onUIFragmentChange: UIFragmentChangeEvent`
|
|
91
|
+
|
|
92
|
+
let uiFragmentSubscriptionSource: GraphQL_Stitcher.subscriptionSource = {
|
|
93
|
+
field: "onUIFragmentChange",
|
|
94
|
+
mutations: [
|
|
95
|
+
"Platform_UIFragmentRegistered",
|
|
96
|
+
"Platform_UIFragmentUpdated",
|
|
97
|
+
"Platform_UIFragmentDeregistered",
|
|
98
|
+
],
|
|
99
|
+
}
|
|
45
100
|
|
|
46
101
|
// Plugin lifecycle Source C subscription. Mirrors the UIFragment pattern: the
|
|
47
102
|
// platform invokes `Platform_PluginStatusChanged` after writing the new status
|
|
48
|
-
// to the Plugin read model;
|
|
49
|
-
//
|
|
103
|
+
// to the Plugin read model; the provider routes that mutation to live
|
|
104
|
+
// `onPluginStatusChange` subscribers (host shell). The status enum
|
|
50
105
|
// matches `PluginsReadModelSpec.status` so consumers can mirror tier 1 / tier 2
|
|
51
106
|
// transitions exactly.
|
|
52
107
|
let pluginStatusSubscriptionTypes = [
|
|
@@ -58,31 +113,46 @@ let pluginStatusMutationFields = [
|
|
|
58
113
|
` Platform_PluginStatusChanged(pluginId: ID!, status: PluginStatus!): PluginStatusChangeEvent`,
|
|
59
114
|
]
|
|
60
115
|
|
|
61
|
-
let pluginStatusSubscriptionField = ` onPluginStatusChange: PluginStatusChangeEvent
|
|
116
|
+
let pluginStatusSubscriptionField = ` onPluginStatusChange: PluginStatusChangeEvent`
|
|
117
|
+
|
|
118
|
+
let pluginStatusSubscriptionSource: GraphQL_Stitcher.subscriptionSource = {
|
|
119
|
+
field: "onPluginStatusChange",
|
|
120
|
+
mutations: ["Platform_PluginStatusChanged"],
|
|
121
|
+
}
|
|
62
122
|
|
|
63
123
|
let baseFragment = (~cloner: bool) => {
|
|
64
|
-
let base = GraphQL_FragmentGenerator.generate(
|
|
124
|
+
let base = GraphQL_FragmentGenerator.generate(
|
|
125
|
+
~mutationEntries=Array.concat(mutationEntries(~cloner), apiFragmentRegistryMutationEntries),
|
|
126
|
+
~queryEntries,
|
|
127
|
+
)
|
|
65
128
|
let parts = GraphQL_Stitcher.decode(base)
|
|
66
129
|
// Source C subscriptions for the admin Plugin aggregate mutations.
|
|
67
130
|
// The standard auto-resolver flow (CommandGeneratorResolvers_AppSync.make)
|
|
68
131
|
// always creates a Subscription.onX resolver per mutation field; without the
|
|
69
132
|
// matching SDL fields, AppSync's CreateResolver fails with "Type not found".
|
|
70
|
-
let pluginAggregateSubscriptionFields =
|
|
133
|
+
let (pluginAggregateSubscriptionFields, pluginAggregateSubscriptionSources) =
|
|
71
134
|
Plugin_SubscriptionSchema.sourceCFields(~mutationEntries=PluginBaseFragment.pluginAggregateMutationEntries)
|
|
72
135
|
GraphQL_Stitcher.encode({
|
|
73
136
|
types: parts.types
|
|
74
137
|
->Array.concat(uiFragmentSubscriptionTypes)
|
|
75
138
|
->Array.concat(pluginStatusSubscriptionTypes)
|
|
76
139
|
->Array.concat(Platform_ComponentDefinitionsApi.sdlTypes)
|
|
77
|
-
->Array.concat(Platform_UIFragmentsApi.sdlTypes)
|
|
140
|
+
->Array.concat(Platform_UIFragmentsApi.sdlTypes)
|
|
141
|
+
->Array.concat(Platform_ApiFragmentsApi.sdlTypes),
|
|
78
142
|
queries: Array.concat(
|
|
79
143
|
parts.queries,
|
|
80
|
-
[
|
|
144
|
+
[
|
|
145
|
+
Platform_ComponentDefinitionsApi.sdlQueryField,
|
|
146
|
+
Platform_UIFragmentsApi.sdlQueryField,
|
|
147
|
+
Platform_ApiFragmentsApi.sdlQueryField,
|
|
148
|
+
],
|
|
81
149
|
),
|
|
82
150
|
mutations: parts.mutations
|
|
83
151
|
->Array.concat(uiFragmentMutationFields)
|
|
84
152
|
->Array.concat(pluginStatusMutationFields),
|
|
85
153
|
subscriptions: [uiFragmentSubscriptionField, pluginStatusSubscriptionField]
|
|
86
154
|
->Array.concat(pluginAggregateSubscriptionFields),
|
|
155
|
+
subscriptionSources: [uiFragmentSubscriptionSource, pluginStatusSubscriptionSource]
|
|
156
|
+
->Array.concat(pluginAggregateSubscriptionSources),
|
|
87
157
|
})
|
|
88
158
|
}
|
|
@@ -4,7 +4,9 @@ import * as S from "sury/src/S.res.mjs";
|
|
|
4
4
|
import * as Api_Naming$ReventlessCore from "../components/Api/Api_Naming.res.mjs";
|
|
5
5
|
import * as GraphQL_Stitcher$ReventlessCore from "../components/Api/GraphQL_Stitcher.res.mjs";
|
|
6
6
|
import * as PluginBaseFragment$ReventlessCore from "../plugin/api/PluginBaseFragment.res.mjs";
|
|
7
|
+
import * as ApiFragmentRegistry$ReventlessCore from "./ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry.res.mjs";
|
|
7
8
|
import * as Platform_UIFragmentsApi$ReventlessCore from "./Platform_UIFragmentsApi.res.mjs";
|
|
9
|
+
import * as Platform_ApiFragmentsApi$ReventlessCore from "./Platform_ApiFragmentsApi.res.mjs";
|
|
8
10
|
import * as GraphQL_FragmentGenerator$ReventlessCore from "../components/Api/GraphQL_FragmentGenerator.res.mjs";
|
|
9
11
|
import * as Plugin_SubscriptionSchema$ReventlessCore from "../plugin/component/Plugin_SubscriptionSchema.res.mjs";
|
|
10
12
|
import * as Platform_ComponentDefinitionsApi$ReventlessCore from "./Platform_ComponentDefinitionsApi.res.mjs";
|
|
@@ -31,6 +33,15 @@ function mutationEntries(cloner) {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
let fieldSpecs = Api_Naming$ReventlessCore.sliceMutationFields("Platform", ApiFragmentRegistry$ReventlessCore.name, ApiFragmentRegistry$ReventlessCore.commandSchema);
|
|
37
|
+
|
|
38
|
+
let apiFragmentRegistryMutationEntries = fieldSpecs.length === 0 ? [] : [{
|
|
39
|
+
fieldNames: fieldSpecs.map(param => param[0]),
|
|
40
|
+
commandSchema: ApiFragmentRegistry$ReventlessCore.commandSchema
|
|
41
|
+
}];
|
|
42
|
+
|
|
43
|
+
let systemCallerFieldNames = apiFragmentRegistryMutationEntries.flatMap(e => e.fieldNames).concat([Platform_ApiFragmentsApi$ReventlessCore.queryFieldName]);
|
|
44
|
+
|
|
34
45
|
let uiFragmentSubscriptionTypes = [
|
|
35
46
|
`enum UIFragmentChangeKind {\n Registered\n Updated\n Deregistered\n}`,
|
|
36
47
|
`type UIFragmentChangeEvent {\n pluginId: ID!\n changeKind: UIFragmentChangeKind!\n manifest: String\n}`
|
|
@@ -42,7 +53,18 @@ let uiFragmentMutationFields = [
|
|
|
42
53
|
` Platform_UIFragmentDeregistered(pluginId: ID!): UIFragmentChangeEvent`
|
|
43
54
|
];
|
|
44
55
|
|
|
45
|
-
let uiFragmentSubscriptionField = ` onUIFragmentChange: UIFragmentChangeEvent
|
|
56
|
+
let uiFragmentSubscriptionField = ` onUIFragmentChange: UIFragmentChangeEvent`;
|
|
57
|
+
|
|
58
|
+
let uiFragmentSubscriptionSource_mutations = [
|
|
59
|
+
"Platform_UIFragmentRegistered",
|
|
60
|
+
"Platform_UIFragmentUpdated",
|
|
61
|
+
"Platform_UIFragmentDeregistered"
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
let uiFragmentSubscriptionSource = {
|
|
65
|
+
field: "onUIFragmentChange",
|
|
66
|
+
mutations: uiFragmentSubscriptionSource_mutations
|
|
67
|
+
};
|
|
46
68
|
|
|
47
69
|
let pluginStatusSubscriptionTypes = [
|
|
48
70
|
`enum PluginStatus {\n Connected\n Disconnected\n Inactive\n Retired\n}`,
|
|
@@ -51,23 +73,35 @@ let pluginStatusSubscriptionTypes = [
|
|
|
51
73
|
|
|
52
74
|
let pluginStatusMutationFields = [` Platform_PluginStatusChanged(pluginId: ID!, status: PluginStatus!): PluginStatusChangeEvent`];
|
|
53
75
|
|
|
54
|
-
let pluginStatusSubscriptionField = ` onPluginStatusChange: PluginStatusChangeEvent
|
|
76
|
+
let pluginStatusSubscriptionField = ` onPluginStatusChange: PluginStatusChangeEvent`;
|
|
77
|
+
|
|
78
|
+
let pluginStatusSubscriptionSource_mutations = ["Platform_PluginStatusChanged"];
|
|
79
|
+
|
|
80
|
+
let pluginStatusSubscriptionSource = {
|
|
81
|
+
field: "onPluginStatusChange",
|
|
82
|
+
mutations: pluginStatusSubscriptionSource_mutations
|
|
83
|
+
};
|
|
55
84
|
|
|
56
85
|
function baseFragment(cloner) {
|
|
57
|
-
let base = GraphQL_FragmentGenerator$ReventlessCore.generate(mutationEntries(cloner), PluginBaseFragment$ReventlessCore.queryEntries);
|
|
86
|
+
let base = GraphQL_FragmentGenerator$ReventlessCore.generate(mutationEntries(cloner).concat(apiFragmentRegistryMutationEntries), PluginBaseFragment$ReventlessCore.queryEntries);
|
|
58
87
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(base);
|
|
59
|
-
let
|
|
88
|
+
let match = Plugin_SubscriptionSchema$ReventlessCore.sourceCFields(PluginBaseFragment$ReventlessCore.pluginAggregateMutationEntries);
|
|
60
89
|
return GraphQL_Stitcher$ReventlessCore.encode({
|
|
61
|
-
types: parts.types.concat(uiFragmentSubscriptionTypes).concat(pluginStatusSubscriptionTypes).concat(Platform_ComponentDefinitionsApi$ReventlessCore.sdlTypes).concat(Platform_UIFragmentsApi$ReventlessCore.sdlTypes),
|
|
90
|
+
types: parts.types.concat(uiFragmentSubscriptionTypes).concat(pluginStatusSubscriptionTypes).concat(Platform_ComponentDefinitionsApi$ReventlessCore.sdlTypes).concat(Platform_UIFragmentsApi$ReventlessCore.sdlTypes).concat(Platform_ApiFragmentsApi$ReventlessCore.sdlTypes),
|
|
62
91
|
mutations: parts.mutations.concat(uiFragmentMutationFields).concat(pluginStatusMutationFields),
|
|
63
92
|
queries: parts.queries.concat([
|
|
64
93
|
Platform_ComponentDefinitionsApi$ReventlessCore.sdlQueryField,
|
|
65
|
-
Platform_UIFragmentsApi$ReventlessCore.sdlQueryField
|
|
94
|
+
Platform_UIFragmentsApi$ReventlessCore.sdlQueryField,
|
|
95
|
+
Platform_ApiFragmentsApi$ReventlessCore.sdlQueryField
|
|
66
96
|
]),
|
|
67
97
|
subscriptions: [
|
|
68
98
|
uiFragmentSubscriptionField,
|
|
69
99
|
pluginStatusSubscriptionField
|
|
70
|
-
].concat(
|
|
100
|
+
].concat(match[0]),
|
|
101
|
+
subscriptionSources: [
|
|
102
|
+
uiFragmentSubscriptionSource,
|
|
103
|
+
pluginStatusSubscriptionSource
|
|
104
|
+
].concat(match[1])
|
|
71
105
|
});
|
|
72
106
|
}
|
|
73
107
|
|
|
@@ -78,12 +112,16 @@ export {
|
|
|
78
112
|
cloneMutationEntry,
|
|
79
113
|
mutationEntries,
|
|
80
114
|
queryEntries,
|
|
115
|
+
apiFragmentRegistryMutationEntries,
|
|
116
|
+
systemCallerFieldNames,
|
|
81
117
|
uiFragmentSubscriptionTypes,
|
|
82
118
|
uiFragmentMutationFields,
|
|
83
119
|
uiFragmentSubscriptionField,
|
|
120
|
+
uiFragmentSubscriptionSource,
|
|
84
121
|
pluginStatusSubscriptionTypes,
|
|
85
122
|
pluginStatusMutationFields,
|
|
86
123
|
pluginStatusSubscriptionField,
|
|
124
|
+
pluginStatusSubscriptionSource,
|
|
87
125
|
baseFragment,
|
|
88
126
|
}
|
|
89
127
|
/* cloneArgsSchema Not a pure module */
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// ApiFragmentRegistry StateChangeSlice — the write side of the platform API-schema
|
|
2
|
+
// fragment registry, replacing the raw `deploy-schema:*` keyspace (see
|
|
3
|
+
// docs/plans/event-sourced-fragment-registries.md).
|
|
4
|
+
//
|
|
5
|
+
// Unlike the UiFragmentRegistry (connect/disconnect-driven), this registry's transitions
|
|
6
|
+
// are driven by the DEPLOY / DESTROY of a plugin stack: the deploy registers its fragment
|
|
7
|
+
// as a SigV4 system caller against the Platform API, and only final retirement
|
|
8
|
+
// (`pulumi destroy`, resolvers gone) deregisters. Version supersession must NOT
|
|
9
|
+
// deregister — the registry is keyed by plugin NAME, so a successor's deploy overwrites
|
|
10
|
+
// its predecessor's fragment in place.
|
|
11
|
+
//
|
|
12
|
+
// The registry is idempotent: re-registering an identical fragment is a no-op,
|
|
13
|
+
// deregistering an absent fragment is a no-op — it never rejects a command.
|
|
14
|
+
//
|
|
15
|
+
// `RecordApiFragmentPush` is the write-back of the single-writer schema-push automation:
|
|
16
|
+
// after stitching and pushing the composed schema it records the outcome (ok / error +
|
|
17
|
+
// message) on the row of the plugin whose fragment change triggered the push, so a
|
|
18
|
+
// deploy waiter can poll the push status instead of timing out on introspection.
|
|
19
|
+
@@reventless.spec
|
|
20
|
+
|
|
21
|
+
open Reventless.Plugin
|
|
22
|
+
|
|
23
|
+
// `at` is the registration timestamp, stamped from the incoming command meta by the
|
|
24
|
+
// dispatching mapping — the StateViewSlice projection has no event meta, so time must
|
|
25
|
+
// ride the payload. Inline records let the ppx auto-tag `pluginId` (scoping the decision
|
|
26
|
+
// read to one plugin name). `message` is a plain string ("" = none) — option fields in
|
|
27
|
+
// variant payloads don't survive the T|null wire contract cleanly.
|
|
28
|
+
//
|
|
29
|
+
// `apiTarget` is the API this plugin's fields belong to — Domain (the default) or Platform.
|
|
30
|
+
// A plugin assigned to the Platform API (e.g. a platform inspector) stitches into the
|
|
31
|
+
// Platform-API schema, not the Domain-API one; the single-writer push automation groups
|
|
32
|
+
// fragments by this target and maintains one cumulative schema per API.
|
|
33
|
+
// `RegisterApiFragment` / `DeregisterApiFragment` ARE the GraphQL surface — the deploy calls them
|
|
34
|
+
// as a SigV4 system caller against the Platform API (exposed as `Platform_RegisterApiFragment` /
|
|
35
|
+
// `Platform_DeregisterApiFragment`). `RecordApiFragmentPush` is `@noApi`: it is the internal
|
|
36
|
+
// write-back of the single-writer schema-push automation, dispatched platform-side, never by a
|
|
37
|
+
// GraphQL caller.
|
|
38
|
+
@schema
|
|
39
|
+
type command =
|
|
40
|
+
| RegisterApiFragment({pluginId: string, fragment: apiSchemaFragment, apiTarget: apiTarget, at: string})
|
|
41
|
+
| DeregisterApiFragment({pluginId: string})
|
|
42
|
+
| @noApi RecordApiFragmentPush({pluginId: string, ok: bool, message: string, at: string})
|
|
43
|
+
|
|
44
|
+
// The registry never rejects (idempotent register/deregister/record); this variant is
|
|
45
|
+
// never returned.
|
|
46
|
+
@schema
|
|
47
|
+
type error = | RegistryError
|
|
48
|
+
|
|
49
|
+
@schema
|
|
50
|
+
type event =
|
|
51
|
+
| ApiFragmentRegistered({pluginId: string, fragment: apiSchemaFragment, apiTarget: apiTarget, at: string})
|
|
52
|
+
| ApiFragmentUpdated({
|
|
53
|
+
pluginId: string,
|
|
54
|
+
previousFragment: apiSchemaFragment,
|
|
55
|
+
newFragment: apiSchemaFragment,
|
|
56
|
+
apiTarget: apiTarget,
|
|
57
|
+
at: string,
|
|
58
|
+
})
|
|
59
|
+
| ApiFragmentDeregistered({pluginId: string})
|
|
60
|
+
| ApiFragmentPushRecorded({pluginId: string, ok: bool, message: string, at: string})
|
|
61
|
+
|
|
62
|
+
// This slice reads exactly the events it writes, so consumedEvent IS event — one inline
|
|
63
|
+
// event type, no duplicate-constructor collision, ppx auto-tags `pluginId` on both roles.
|
|
64
|
+
@schema
|
|
65
|
+
type consumedEvent = event
|