@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 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
- [![npm version](https://img.shields.io/npm/v/@reventlessdev/reventless-spec.svg?label=version)](https://www.npmjs.com/package/@reventlessdev/reventless-spec)
2
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
- [![Changelog](https://img.shields.io/badge/📋-Changelog-blue)](./CHANGELOG.md)
4
-
5
- # `reventless-spec`
6
-
7
- Specification types and interfaces for Reventless applications.
8
-
9
- ## Usage
10
-
11
- - Add `@reventlessdev/reventless-spec` to your dependencies in `package.json`.
12
- - Add `@reventlessdev/reventless-spec` to your dependencies in `rescript.json`.
13
- - For general information see this monorepo's [readme](../../README.md)
14
-
15
- ## Installation
1
+ [![npm](https://img.shields.io/npm/v/@reventlessdev/reventless-spec.svg?label=npm)](https://www.npmjs.com/package/@reventlessdev/reventless-spec)
2
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
3
+ [![Docs](https://img.shields.io/badge/docs-reventless.dev-blue)](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
- npm install @reventlessdev/reventless-spec
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
- ## `generate-plugin` CLI
65
+ ## Where it fits
32
66
 
33
- `reventless-spec` ships a `generate-plugin` binary that auto-generates `src/Plugin.res` from a plugin's folder structure. This eliminates the need to maintain a hand-authored composition root.
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
- ### Usage
50
-
51
- ```bash
52
- generate-plugin src/ # generate src/Plugin.res from the src/ folder
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
- ### Folder conventions
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
- The generator classifies `.res` files by their parent folder name. Chapter folders (e.g. `Product/`, `Customer/`) are transparent — only the leaf folder name matters.
82
+ You normally obtain `reventless-spec` transitively by scaffolding an app rather than
83
+ installing it on its own.
59
84
 
60
- | Folder | Component |
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
- Always excluded: `Plugin/`, `tests/`, `lib/`, `*Test.res`, `*Fixtures.res`.
74
-
75
- ### `plugin.json` (optional)
87
+ ```bash
88
+ pnpm add @reventlessdev/reventless-spec
89
+ ```
76
90
 
77
- Place `src/plugin.json` to override defaults:
91
+ Then register it as a ReScript dependency in `rescript.json`:
78
92
 
79
93
  ```json
80
94
  {
81
- "name": "Catalog",
82
- "heartbeatInterval": 60,
83
- "exclude": ["Product/StateChangeSlice/Experimental.res", "Analytics/**"]
95
+ "dependencies": ["@reventlessdev/reventless-spec"]
84
96
  }
85
97
  ```
86
98
 
87
- | Field | Default |
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
- The generator references it as `OrdersExtension.Mapping`.
101
+ ## Links
109
102
 
110
- ### Namespace note
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
- Plugin packages should use a **suffixed namespace** (e.g. `CatalogPlugin`, `OrderingPlugin`). Never use a bare name like `Ordering` — it shadows OCaml's built-in `Ordering` type used by comparisons.
107
+ ## License
113
108
 
114
- The generated `Plugin.res` is **committed to git** — CI compiles it directly without re-running the generator.
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.74",
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.6"
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
  }