@reventlessdev/reventless-spec 3.0.0-alpha.74 → 3.0.0-alpha.76
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 +15 -0
- package/README.md +85 -90
- package/package.json +2 -2
- package/src/components/Plugin.res +10 -2
- package/src/components/Plugin.res.mjs +6 -1
- package/src/generator/Codegen.res +7 -0
- package/src/generator/Codegen.res.mjs +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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.76 (2026-07-12)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **admin:** make the API fragment registry per-target (Domain | Platform) ([30491a9](https://github.com/ReventlessDev/reventless-core/commit/30491a9e14b4236c98cc756efb6de68ede1e77d7))
|
|
11
|
+
* **admin:** retire the Plugin-aggregate UI-fragment path in favour of the registry slices ([1dbc708](https://github.com/ReventlessDev/reventless-core/commit/1dbc708e7439b34ff970cc3d963d7835a8c6fd48))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 3.0.0-alpha.75 (2026-07-11)
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* **infra:** add DeployBootstrap seam for generated deploy programs ([f0dc868](https://github.com/ReventlessDev/reventless-core/commit/f0dc8686396d21e7b39667eb0825b2e57fe4dabf))
|
|
19
|
+
|
|
20
|
+
|
|
6
21
|
# 3.0.0-alpha.74 (2026-07-11)
|
|
7
22
|
|
|
8
23
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -1,114 +1,109 @@
|
|
|
1
|
-
[](https://www.npmjs.com/package/@reventlessdev/reventless-spec)
|
|
2
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
3
|
+
[](https://docs.reventless.dev)
|
|
4
|
+
|
|
5
|
+
# @reventlessdev/reventless-spec
|
|
6
|
+
|
|
7
|
+
> ⚠️ **Alpha.** APIs and on-disk formats can change without notice between releases.
|
|
8
|
+
> Pin exact versions and expect breaking changes.
|
|
9
|
+
|
|
10
|
+
The **specification layer** of [Reventless](https://docs.reventless.dev) — a
|
|
11
|
+
spec-driven, event-sourced CQRS framework written in [ReScript](https://rescript-lang.org).
|
|
12
|
+
This package holds the type specifications and module-type interfaces that the rest of the
|
|
13
|
+
framework is built on: the contracts application domain code implements (commands, events,
|
|
14
|
+
behaviors, projections) and the component-kind vocabulary the whole framework shares. It
|
|
15
|
+
has **no** runtime, cloud, or storage dependency, so domain code can declare its shape
|
|
16
|
+
against `reventless-spec` without pulling in an implementation. Modules are exposed under
|
|
17
|
+
the `Reventless` namespace.
|
|
18
|
+
|
|
19
|
+
## What it provides
|
|
20
|
+
|
|
21
|
+
ReScript modules, consumed by adding the package to your `rescript.json` `dependencies`.
|
|
22
|
+
|
|
23
|
+
**Domain contracts (`types/`)**
|
|
24
|
+
|
|
25
|
+
- **`Message`** — the envelope metadata carried by every event and command (`meta`,
|
|
26
|
+
`event'<>`, `command'<>`), used for audit, correlation, tracing, and routing.
|
|
27
|
+
- **`Behavior`** — the module type application code implements to define aggregate business
|
|
28
|
+
logic: `initialState`, `evolve(state, event)`, and `decide(command, state)`.
|
|
29
|
+
- **`Projection`** — `Source` / `Target` module types describing a read model projected
|
|
30
|
+
from an aggregate's events.
|
|
31
|
+
- **`Handler`** — function signatures for handling commands, events, and errors.
|
|
32
|
+
- **`SideEffect`** — module type for an imperative side effect triggered by aggregate events
|
|
33
|
+
(read-only access to the query engine; emits no events).
|
|
34
|
+
- **`StoredEvent`** — the logical envelope for an event as it lives in storage; single
|
|
35
|
+
source of truth for the on-disk event shape.
|
|
36
|
+
- **`Authorization`** / **`Identity`** — provider-agnostic permission rules evaluated against
|
|
37
|
+
a resolved identity.
|
|
38
|
+
- **`QueryEngine`** — typed filter values and operations for read-model queries.
|
|
39
|
+
- **`Id`**, **`EventMapping`**, **`ReadConsistency`**, **`Schedule`**, **`Visibility`** —
|
|
40
|
+
supporting spec types for identifiers, event translation, consistency, scheduling, and
|
|
41
|
+
exposure.
|
|
42
|
+
|
|
43
|
+
**Component vocabulary (`components/`)**
|
|
44
|
+
|
|
45
|
+
- **`ComponentKind`** — the single source of truth for the Reventless component-kind
|
|
46
|
+
vocabulary (aggregates, read models, tasks, extension points, DCB slices), including every
|
|
47
|
+
accepted folder spelling and body-file suffix.
|
|
48
|
+
- Per-kind spec modules: `Aggregate`, `ReadModel`, `Task`, `ExtensionPoint`, `Plugin`, and
|
|
49
|
+
the DCB slices (`StateChangeSlice`, `StateViewSlice`, `AutomationSlice`,
|
|
50
|
+
`InboundTranslationSlice`, `OutboundTranslationSlice`), plus DCB tagging/validation helpers.
|
|
51
|
+
|
|
52
|
+
**`generate-plugin` CLI**
|
|
53
|
+
|
|
54
|
+
`reventless-spec` ships a `generate-plugin` binary that auto-generates `src/Plugin.res` from
|
|
55
|
+
a plugin's folder structure, classifying `.res` files by their parent folder name — no
|
|
56
|
+
hand-authored composition root required.
|
|
16
57
|
|
|
17
58
|
```bash
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Add to your `rescript.json`:
|
|
22
|
-
|
|
23
|
-
```json
|
|
24
|
-
{
|
|
25
|
-
"bs-dependencies": ["@reventlessdev/reventless-spec"]
|
|
26
|
-
}
|
|
59
|
+
generate-plugin src/ # generate src/Plugin.res from the src/ folder
|
|
27
60
|
```
|
|
28
61
|
|
|
29
|
-
|
|
62
|
+
Wire it into your build via a `prebuild` script so it runs before `rescript build`. The
|
|
63
|
+
generated `Plugin.res` is committed to git, so CI compiles it directly.
|
|
30
64
|
|
|
31
|
-
##
|
|
65
|
+
## Where it fits
|
|
32
66
|
|
|
33
|
-
`reventless-spec`
|
|
67
|
+
`reventless-spec` is the foundation package in the Reventless framework:
|
|
34
68
|
|
|
35
|
-
### Setup
|
|
36
|
-
|
|
37
|
-
Add to your plugin's `package.json`:
|
|
38
|
-
|
|
39
|
-
```json
|
|
40
|
-
{
|
|
41
|
-
"scripts": {
|
|
42
|
-
"generate": "generate-plugin src/",
|
|
43
|
-
"prebuild": "npm run generate",
|
|
44
|
-
"build": "rescript build"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
69
|
```
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
npm run build # prebuild runs generate automatically, then compiles
|
|
70
|
+
reventless-spec (specifications) ← this package
|
|
71
|
+
↓
|
|
72
|
+
reventless-core (provider-agnostic framework)
|
|
73
|
+
↓
|
|
74
|
+
reventless-aws / reventless-postgres / reventless-local (storage & deployment adapters)
|
|
54
75
|
```
|
|
55
76
|
|
|
56
|
-
|
|
77
|
+
- [`@reventlessdev/reventless-core`](https://www.npmjs.com/package/@reventlessdev/reventless-core)
|
|
78
|
+
builds on these specs to provide the runtime building blocks.
|
|
79
|
+
- [`@reventlessdev/reventless-infra`](https://www.npmjs.com/package/@reventlessdev/reventless-infra)
|
|
80
|
+
layers deploy-time infrastructure types on top of the same specs.
|
|
57
81
|
|
|
58
|
-
|
|
82
|
+
You normally obtain `reventless-spec` transitively by scaffolding an app rather than
|
|
83
|
+
installing it on its own.
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|---|---|
|
|
62
|
-
| `Aggregate[s]` | Aggregate — must be paired with a `*Behavior.res` |
|
|
63
|
-
| `ReadModel[s]` | Read model — must be paired with a `*Projections.res` |
|
|
64
|
-
| `Task[s]` | Task |
|
|
65
|
-
| `ExtensionPoint[s]` | Extension point mapping |
|
|
66
|
-
| `Extension[s]` | Extension mapping |
|
|
67
|
-
| `StateChange[s][Slice[s]]` | DCB StateChangeSlice |
|
|
68
|
-
| `StateView[s][Slice[s]]` | DCB StateViewSlice |
|
|
69
|
-
| `Automation[s][Slice[s]]` | DCB AutomationSlice |
|
|
70
|
-
| `InboundTranslation[s][Slice[s]]` | DCB InboundTranslationSlice |
|
|
71
|
-
| `OutboundTranslation[s][Slice[s]]` | DCB OutboundTranslationSlice |
|
|
85
|
+
## Install
|
|
72
86
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
```bash
|
|
88
|
+
pnpm add @reventlessdev/reventless-spec
|
|
89
|
+
```
|
|
76
90
|
|
|
77
|
-
|
|
91
|
+
Then register it as a ReScript dependency in `rescript.json`:
|
|
78
92
|
|
|
79
93
|
```json
|
|
80
94
|
{
|
|
81
|
-
"
|
|
82
|
-
"heartbeatInterval": 60,
|
|
83
|
-
"exclude": ["Product/StateChangeSlice/Experimental.res", "Analytics/**"]
|
|
95
|
+
"dependencies": ["@reventlessdev/reventless-spec"]
|
|
84
96
|
}
|
|
85
97
|
```
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
|---|---|
|
|
89
|
-
| `name` | Derived from `package.json` `"name"` — unscoped, hyphens/underscores → PascalCase |
|
|
90
|
-
| `heartbeatInterval` | `60` |
|
|
91
|
-
| `exclude` | `[]` — file paths or globs relative to `src/` |
|
|
92
|
-
|
|
93
|
-
### Extension file convention
|
|
94
|
-
|
|
95
|
-
Each file in `Extension/` must expose its mapping as `module Mapping`:
|
|
96
|
-
|
|
97
|
-
```rescript
|
|
98
|
-
// OrdersExtension.res
|
|
99
|
-
open ReventlessInfra.ExtensionMapping
|
|
100
|
-
|
|
101
|
-
module Mapping = {
|
|
102
|
-
module ExtensionPoint = OrderingSpec.OrdersExtensionPoint
|
|
103
|
-
module Delegate = ProductDemand
|
|
104
|
-
// ...
|
|
105
|
-
}
|
|
106
|
-
```
|
|
99
|
+
Requires ReScript `^12.3.0` (peer dependency).
|
|
107
100
|
|
|
108
|
-
|
|
101
|
+
## Links
|
|
109
102
|
|
|
110
|
-
|
|
103
|
+
- 📚 Documentation — [docs.reventless.dev](https://docs.reventless.dev)
|
|
104
|
+
- 📦 Repository — [ReventlessDev/reventless-core](https://github.com/ReventlessDev/reventless-core)
|
|
105
|
+
- 📋 [Changelog](./CHANGELOG.md)
|
|
111
106
|
|
|
112
|
-
|
|
107
|
+
## License
|
|
113
108
|
|
|
114
|
-
|
|
109
|
+
[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-spec",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.76",
|
|
4
4
|
"description": "Specifications for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"rescript": "^12.3.0",
|
|
25
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.
|
|
25
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.7"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"rescript": "^12.3.0"
|
|
@@ -92,6 +92,16 @@ Encoded as JSON for transport; protocol identifies the schema format (e.g. "grap
|
|
|
92
92
|
@schema
|
|
93
93
|
type apiSchemaFragment = {encoded: string, protocol: string}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
The API a plugin's GraphQL fields are stitched into: the Domain API (the default —
|
|
97
|
+
application plugins) or the Platform API (platform-level plugins such as an inspector,
|
|
98
|
+
which contribute fields alongside the admin base). Serializes as the bare string
|
|
99
|
+
"Domain" / "Platform". Consumed by the schema-fragment registry to maintain one
|
|
100
|
+
cumulative schema per API.
|
|
101
|
+
*/
|
|
102
|
+
@schema
|
|
103
|
+
type apiTarget = Domain | Platform
|
|
104
|
+
|
|
95
105
|
// Sury's nullableAsOption creates T | undefined | null which fails jsonableValidation
|
|
96
106
|
// inside union variant payloads. js_nullable creates T | null (no undefined) which is
|
|
97
107
|
// JSON-safe and passes jsonableValidation in all contexts.
|
|
@@ -403,8 +413,6 @@ type pluginDefinition = {
|
|
|
403
413
|
// Uses @s.matches(stringOptionSchema) — js_nullable creates string | null (not string | undefined),
|
|
404
414
|
// which passes sury's jsonableValidation inside union variant payloads.
|
|
405
415
|
apiTarget: @s.matches(stringOptionSchema) option<string>,
|
|
406
|
-
// UI fragment manifest contributed by this plugin (optional, absent for pure backend plugins).
|
|
407
|
-
uiFragments: @s.matches(uiFragmentManifestOptionSchema) option<uiFragmentManifest>,
|
|
408
416
|
// Component graph metadata — populated by makePluginDefinition; absent for older protocol versions.
|
|
409
417
|
structure: @s.matches(pluginStructureOptionSchema) option<pluginStructure>,
|
|
410
418
|
// DCB EventLog definition for plugins that bundle a DcbEventLog component.
|
|
@@ -38,6 +38,11 @@ let apiSchemaFragmentSchema = S.schema(s => ({
|
|
|
38
38
|
protocol: s.m(S.string)
|
|
39
39
|
}));
|
|
40
40
|
|
|
41
|
+
let apiTargetSchema = S.union([
|
|
42
|
+
S.literal("Domain"),
|
|
43
|
+
S.literal("Platform")
|
|
44
|
+
]);
|
|
45
|
+
|
|
41
46
|
let apiSchemaFragmentOptionSchema = SuryResMjs.js_nullable(apiSchemaFragmentSchema);
|
|
42
47
|
|
|
43
48
|
let dcbEventLogOptionSchema = SuryResMjs.js_nullable(dcbEventLogDefinitionSchema);
|
|
@@ -214,7 +219,6 @@ let pluginDefinitionSchema = S.schema(s => ({
|
|
|
214
219
|
extensionProtocols: s.m(S.array(extensionProtocolSchema)),
|
|
215
220
|
apiSchemaFragment: s.m(apiSchemaFragmentOptionSchema),
|
|
216
221
|
apiTarget: s.m(stringOptionSchema),
|
|
217
|
-
uiFragments: s.m(uiFragmentManifestOptionSchema),
|
|
218
222
|
structure: s.m(pluginStructureOptionSchema),
|
|
219
223
|
dcbEventLog: s.m(dcbEventLogOptionSchema),
|
|
220
224
|
kind: s.m(pluginKindSchema)
|
|
@@ -233,6 +237,7 @@ export {
|
|
|
233
237
|
dcbEventLogDefinitionSchema,
|
|
234
238
|
extensionProtocolSchema,
|
|
235
239
|
apiSchemaFragmentSchema,
|
|
240
|
+
apiTargetSchema,
|
|
236
241
|
apiSchemaFragmentOptionSchema,
|
|
237
242
|
dcbEventLogOptionSchema,
|
|
238
243
|
stringOptionSchema,
|
|
@@ -458,6 +458,10 @@ let renderMain = (~config: Config.config): string => {
|
|
|
458
458
|
"// AUTO-GENERATED — do not edit. Run `npm run generate` to update.",
|
|
459
459
|
"// " ++ name ++ " plugin — AWS deployment.",
|
|
460
460
|
"",
|
|
461
|
+
// Deploy-time bootstrap seam (no-op unless a package registers a
|
|
462
|
+
// contribution). PreDeploy runs before the platform/plugin graph builds.
|
|
463
|
+
"ReventlessInfra.DeployBootstrap.run(PreDeploy)",
|
|
464
|
+
"",
|
|
461
465
|
"module Platform = ReventlessAws.Platform.Make()",
|
|
462
466
|
"module " ++ name ++ " = Plugin.Make(Platform)",
|
|
463
467
|
"",
|
|
@@ -465,6 +469,9 @@ let renderMain = (~config: Config.config): string => {
|
|
|
465
469
|
" ~plugin=module(" ++ name ++ "),",
|
|
466
470
|
")",
|
|
467
471
|
"",
|
|
472
|
+
// PostDeploy runs after the graph is registered (exports, cross-stack output).
|
|
473
|
+
"ReventlessInfra.DeployBootstrap.run(PostDeploy)",
|
|
474
|
+
"",
|
|
468
475
|
]->Array.join("\n")
|
|
469
476
|
}
|
|
470
477
|
|
|
@@ -300,12 +300,16 @@ function renderMain(config) {
|
|
|
300
300
|
"// AUTO-GENERATED — do not edit. Run `npm run generate` to update.",
|
|
301
301
|
"// " + name + " plugin — AWS deployment.",
|
|
302
302
|
"",
|
|
303
|
+
"ReventlessInfra.DeployBootstrap.run(PreDeploy)",
|
|
304
|
+
"",
|
|
303
305
|
"module Platform = ReventlessAws.Platform.Make()",
|
|
304
306
|
"module " + name + " = Plugin.Make(Platform)",
|
|
305
307
|
"",
|
|
306
308
|
"let default = Platform.deployPlugin(",
|
|
307
309
|
" ~plugin=module(" + name + "),",
|
|
308
310
|
")",
|
|
311
|
+
"",
|
|
312
|
+
"ReventlessInfra.DeployBootstrap.run(PostDeploy)",
|
|
309
313
|
""
|
|
310
314
|
].join("\n");
|
|
311
315
|
}
|