@reventlessdev/reventless-spec 3.0.0-alpha.115 → 3.0.0-alpha.117
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 +17 -0
- package/package.json +2 -2
- package/schema/platform-api.graphql +11 -2
- package/src/components/DcbScopeInference.res +1 -1
- package/src/components/InboundTranslationSlice.res +1 -1
- package/src/components/OutboundTranslationSlice.res +1 -1
- package/src/components/Plugin.res +28 -7
- package/src/components/Plugin.res.mjs +1 -0
- package/src/components/Snapshot.res +1 -1
- package/src/components/StateAnnotations.res +25 -3
- package/src/generator/Discovery.res +1 -1
- package/src/types/Behavior.res +1 -1
- package/src/types/Message.res +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
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.117 (2026-08-18)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **api:** declare the reference door in the SDL every backend is built from ([5c1857e](https://github.com/ReventlessDev/reventless-core/commit/5c1857ea90ff40305a1c44a9e57043528e6a93aa))
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **core:** let a reference name a retired row, and let an elevated caller open one ([9e2623a](https://github.com/ReventlessDev/reventless-core/commit/9e2623a4b22487561607fcc0ca19d51726069ee4))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# 3.0.0-alpha.116 (2026-08-16)
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **ppx:** declare a command's lifecycle edge once, as [@transition](https://github.com/transition) ([dd35130](https://github.com/ReventlessDev/reventless-core/commit/dd3513014f14b71879ee21263c6755d3c3d95096))
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
# 3.0.0-alpha.115 (2026-08-16)
|
|
7
24
|
|
|
8
25
|
### Features
|
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.117",
|
|
4
4
|
"description": "Specifications for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"sury": "11.0.0-alpha.4",
|
|
22
22
|
"sury-ppx": "11.0.0-alpha.2",
|
|
23
23
|
"yaml": "^2.8.3",
|
|
24
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.
|
|
24
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.8"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"rescript": "12.3.0",
|
|
@@ -178,6 +178,13 @@ enum Platform_PluginKind {
|
|
|
178
178
|
PlatformInfrastructure
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
type Platform_PluginRef {
|
|
182
|
+
id: ID!
|
|
183
|
+
label: String!
|
|
184
|
+
retired: Boolean!
|
|
185
|
+
retiredState: String
|
|
186
|
+
}
|
|
187
|
+
|
|
181
188
|
enum Platform_PluginStatus {
|
|
182
189
|
Connected
|
|
183
190
|
Disconnected
|
|
@@ -215,6 +222,7 @@ type Platform_ReadSideDef {
|
|
|
215
222
|
lifecycleField: String
|
|
216
223
|
linkedWriteSide: [String!]!
|
|
217
224
|
name: String!
|
|
225
|
+
namedWhenRetired: Boolean!
|
|
218
226
|
ownerField: String
|
|
219
227
|
queryField: String!
|
|
220
228
|
requiredAccess: [String!]
|
|
@@ -290,10 +298,11 @@ type PluginStatusChangeEvent {
|
|
|
290
298
|
|
|
291
299
|
type Query {
|
|
292
300
|
Platform_ComponentDefinitions: [Platform_ComponentDefinitionEntry!]!
|
|
293
|
-
Platform_Plugin(id: ID
|
|
301
|
+
Platform_Plugin(id: ID!, includeRetired: Boolean): Platform_Plugin
|
|
294
302
|
Platform_PluginStructures: [Platform_PluginStructureEntry!]!
|
|
295
303
|
Platform_Plugins(after: String, before: String, filter: Platform_PluginFilter, first: Int, includeRetired: Boolean, last: Int): Platform_PluginConnection!
|
|
296
|
-
Platform_PluginsByIds(ids: [String!]
|
|
304
|
+
Platform_PluginsByIds(ids: [String!]!, includeRetired: Boolean): [Platform_Plugin!]!
|
|
305
|
+
Platform_PluginsRefs(ids: [ID!]!): [Platform_PluginRef!]!
|
|
297
306
|
Platform_UIFragments: [Platform_UIFragmentEntry!]!
|
|
298
307
|
}
|
|
299
308
|
|
|
@@ -8,7 +8,7 @@ each slice (variant names + `*Id`-shaped fields), never an `S.t` schema and neve
|
|
|
8
8
|
a tag-metadata flag. That is precisely what we are replacing — so both the runtime
|
|
9
9
|
(building shapes from `S.t` schemas, via `DcbTag.sliceShapeFromSchemas`) and the
|
|
10
10
|
VS Code tooling (building shapes from parsed `.res` source) can feed the same
|
|
11
|
-
`infer`. See `docs/plans/dcb-tag-scope-inference.md` § "Phase 1 design".
|
|
11
|
+
`infer`. See `docs/plans/done/dcb-tag-scope-inference.md` § "Phase 1 design".
|
|
12
12
|
|
|
13
13
|
The three rules (over the representation):
|
|
14
14
|
|
|
@@ -53,7 +53,7 @@ module type Spec = {
|
|
|
53
53
|
|
|
54
54
|
/** Optional display name of the foreign system this anti-corruption slice receives
|
|
55
55
|
from (e.g. `"SupplierFeed"`). Drives the **external box** drawn outside the plugin
|
|
56
|
-
in the Event Graph / Context Map
|
|
56
|
+
in the Event Graph / Context Map.
|
|
57
57
|
Auto-injected by `@@reventless.spec` defaulting to `None` — set it to name the box. */
|
|
58
58
|
let externalSystem: option<string>
|
|
59
59
|
|
|
@@ -103,7 +103,7 @@ module type Spec = {
|
|
|
103
103
|
|
|
104
104
|
/** Optional display name of the foreign system this anti-corruption slice publishes
|
|
105
105
|
to (e.g. `"EmailService"`). Drives the **external box** drawn outside the plugin
|
|
106
|
-
in the Event Graph / Context Map
|
|
106
|
+
in the Event Graph / Context Map.
|
|
107
107
|
Auto-injected by `@@reventless.spec` defaulting to `None` — set it to name the box. */
|
|
108
108
|
let externalSystem: option<string>
|
|
109
109
|
}
|
|
@@ -180,11 +180,12 @@ type commandDef = {
|
|
|
180
180
|
/**
|
|
181
181
|
The single lifecycle state this command's handler writes — the command's *to*
|
|
182
182
|
state, sibling of `allowedStates`' *from* set. Source: the
|
|
183
|
-
`@
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
target of the `@transition(([Placed]) => Shipped)` command-variant annotation.
|
|
184
|
+
`Some("Shipped")` lets a resolver move a row by a declared transition instead
|
|
185
|
+
of a guess. `None` means no target was declared — and note that this is two
|
|
186
|
+
different statements depending on `allowedStates`: with a from-set present it
|
|
187
|
+
is the command declaring it does not move the row, and with none it is simply
|
|
188
|
+
an unannotated command. js_nullable for JSON safety, same as `allowedStates`.
|
|
188
189
|
*/
|
|
189
190
|
targetState: @s.matches(stringOptionSchema) option<string>,
|
|
190
191
|
/**
|
|
@@ -318,12 +319,32 @@ type queryableDef = {
|
|
|
318
319
|
state schema: a client holding this def holds the whole predicate, and two places
|
|
319
320
|
deriving one comparison is how they come to disagree about it.
|
|
320
321
|
|
|
321
|
-
The state form is also what lets `@
|
|
322
|
-
when retired, with no annotation beyond the
|
|
322
|
+
The state form is also what lets a command's `@transition` answer applicability
|
|
323
|
+
when retired, with no annotation beyond the one — retirement expressed in the
|
|
323
324
|
vocabulary a command's stance is already written in.
|
|
324
325
|
*/
|
|
325
326
|
retiredValues: @s.matches(stringArrayOptionSchema) option<array<string>>,
|
|
326
327
|
/**
|
|
328
|
+
Whether this view publishes a **reference door**: the by-ids read that names a
|
|
329
|
+
retired row for any caller holding a pointer to it, projected to the row's id,
|
|
330
|
+
its `labelField` and the value of `retiredField`. Declared with
|
|
331
|
+
`@namedWhenRetired` on the state record.
|
|
332
|
+
|
|
333
|
+
Published so a client knows the door exists without probing for it — a query
|
|
334
|
+
against a field the schema does not have is a validation error, not an empty
|
|
335
|
+
answer, so "ask and see" is not a usable fallback here.
|
|
336
|
+
|
|
337
|
+
Nullable rather than a bare required bool, which is the rule this schema's own
|
|
338
|
+
tripwire enforces: a definition stored before the field existed would otherwise
|
|
339
|
+
decode with an invented value and a runtime warning. Absent and `false` mean the
|
|
340
|
+
same thing to every reader — the archive stays shut — but only one of them is
|
|
341
|
+
something the platform actually said.
|
|
342
|
+
|
|
343
|
+
It is never `true` without `retiredField`: the PPX refuses the annotation on a
|
|
344
|
+
record with no retirement.
|
|
345
|
+
*/
|
|
346
|
+
namedWhenRetired: @s.matches(boolOptionSchema) option<bool>,
|
|
347
|
+
/**
|
|
327
348
|
Component visibility hint (`@@reventless.visibility`). `Some("Internal")` marks a
|
|
328
349
|
ReadModel / StateViewSlice that the deployed AutoUI hides from its menu, drill-down
|
|
329
350
|
pages, web event graph and cross-plugin edges. `None` (absent) means Public. Internal
|
|
@@ -122,6 +122,7 @@ let queryableDefSchema = S.schema(s => ({
|
|
|
122
122
|
ownerField: s.m(stringOptionSchema),
|
|
123
123
|
retiredField: s.m(stringOptionSchema),
|
|
124
124
|
retiredValues: s.m(stringArrayOptionSchema),
|
|
125
|
+
namedWhenRetired: s.m(boolOptionSchema),
|
|
125
126
|
visibility: s.m(stringOptionSchema),
|
|
126
127
|
chapter: s.m(stringOptionSchema),
|
|
127
128
|
singleQueryField: s.m(stringOptionSchema),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Per-aggregate persisted-snapshot configuration
|
|
2
|
-
// (docs/plans/aggregate-snapshotting.md).
|
|
2
|
+
// (docs/plans/done/aggregate-snapshotting.md).
|
|
3
3
|
//
|
|
4
4
|
// Snapshotting is opt-in and lives on the Behavior module (where `state` is
|
|
5
5
|
// defined): `Behavior.T.snapshot = None` (the default, auto-injected by
|
|
@@ -52,8 +52,9 @@ otherwise see the same negative marker on every record they can read.
|
|
|
52
52
|
two-valued enum.
|
|
53
53
|
- `Some(v)` — the **state** form. The row is retired when the field equals `v`,
|
|
54
54
|
and the field is the record's `@lifecycle` field. One field then carries the
|
|
55
|
-
fact once instead of twice, and `@
|
|
56
|
-
command is enough to make a generated menu offer it there and nowhere
|
|
55
|
+
fact once instead of twice, and `@transition(([Deactivated]) => Active)` on the
|
|
56
|
+
way-back command is enough to make a generated menu offer it there and nowhere
|
|
57
|
+
else —
|
|
57
58
|
because retirement is finally expressible in the vocabulary that stance is
|
|
58
59
|
already written in.
|
|
59
60
|
|
|
@@ -80,6 +81,27 @@ type retiredSpec = {
|
|
|
80
81
|
most visibly — would stop firing on a value that looks merely empty.
|
|
81
82
|
*/
|
|
82
83
|
values: option<array<string>>,
|
|
84
|
+
/**
|
|
85
|
+
Whether a reference to a retired row of this record still resolves — the
|
|
86
|
+
`@namedWhenRetired` opt-in on the `@schema type state` declaration.
|
|
87
|
+
|
|
88
|
+
Retirement withholds a row from every door at once, which is the right answer
|
|
89
|
+
to "what may this caller browse" and an unasked answer to "what is the row this
|
|
90
|
+
caller is already holding a reference to called". An order names a product it
|
|
91
|
+
bought; archiving the product should not unname it on the order.
|
|
92
|
+
|
|
93
|
+
`true` opens exactly one door: a retired row answers a by-ids reference read
|
|
94
|
+
with its id, its label field and the value of `field` — and nothing else, for
|
|
95
|
+
any caller. It does not widen the list, the single-entity read, the index reads
|
|
96
|
+
or the live frame, and it does not touch the owner rule: a retired row that is
|
|
97
|
+
owner-scoped still resolves for its owner alone.
|
|
98
|
+
|
|
99
|
+
Inside `retiredSpec` rather than beside it, because it is a rule about withheld
|
|
100
|
+
rows and there are none without a retirement — the PPX errors on the annotation
|
|
101
|
+
when the record declares no `@retired`, so the nesting states a guarantee rather
|
|
102
|
+
than a convention.
|
|
103
|
+
*/
|
|
104
|
+
namedWhenRetired: bool,
|
|
83
105
|
}
|
|
84
106
|
|
|
85
107
|
type stateAnnotationSpec = {
|
|
@@ -113,7 +135,7 @@ type stateAnnotationSpec = {
|
|
|
113
135
|
metric: array<(string, metricSpec)>,
|
|
114
136
|
/**
|
|
115
137
|
Field annotated `@lifecycle` on the state record (PPX-emitted) — the enum a
|
|
116
|
-
command's `@
|
|
138
|
+
command's `@transition` is written in terms of, a board draws its columns
|
|
117
139
|
from and a state diagram renders. `Some(name)` when one such annotation
|
|
118
140
|
exists; the PPX errors on duplicate `@lifecycle` annotations within the same
|
|
119
141
|
record. Codegen consumes this to populate `queryableDef.lifecycleField`, and
|
|
@@ -29,7 +29,7 @@ let folderToComponentType = ComponentKind.folderToKind
|
|
|
29
29
|
// `None`. Uses the single-source `ComponentKind.isKindFolder`, so a chapter read
|
|
30
30
|
// here (build time, disk) agrees with the authoring tool's identical heuristic and
|
|
31
31
|
// with a chapter reflected off the deployed plugin structure. See
|
|
32
|
-
// docs/plans/deployed-chapter-grouping.md.
|
|
32
|
+
// docs/plans/done/deployed-chapter-grouping.md.
|
|
33
33
|
let chapterOf = (relPath: string): option<string> => {
|
|
34
34
|
let segments = relPath->String.split("/")
|
|
35
35
|
// Need at least one directory segment before the filename.
|
package/src/types/Behavior.res
CHANGED
|
@@ -72,7 +72,7 @@ module type T = {
|
|
|
72
72
|
*/
|
|
73
73
|
let decide: (state, Spec.command) => result<array<Spec.event>, Spec.error>
|
|
74
74
|
|
|
75
|
-
/** Persisted-snapshot configuration (docs/plans/aggregate-snapshotting.md).
|
|
75
|
+
/** Persisted-snapshot configuration (docs/plans/done/aggregate-snapshotting.md).
|
|
76
76
|
`None` — the default, auto-injected by `@@reventless.behavior` — keeps
|
|
77
77
|
full replay; `Some({interval, stateSchema})` writes a keep-one snapshot
|
|
78
78
|
every `interval` events and seeds cold replays from the latest one.
|
package/src/types/Message.res
CHANGED
|
@@ -162,7 +162,7 @@ type commandJson = {
|
|
|
162
162
|
// (clones via a JSON round-trip; never re-encodes through the schema), is idempotent on
|
|
163
163
|
// valid data, and falls back to the ORIGINAL error when the fill doesn't resolve the
|
|
164
164
|
// failure — so genuine corruption still surfaces.
|
|
165
|
-
// See docs/plans/platform-infrastructure-in-plugin-list.md (durable fix option 2).
|
|
165
|
+
// See docs/plans/done/platform-infrastructure-in-plugin-list.md (durable fix option 2).
|
|
166
166
|
//
|
|
167
167
|
// The scalar arm is the odd one out and is deliberately noisy. Every other fill is
|
|
168
168
|
// *derived* — the schema states what an absent value means, and the fill supplies exactly
|