@reventlessdev/reventless-spec 3.0.0-alpha.67 → 3.0.0-alpha.69
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 +14 -0
- package/package.json +1 -1
- package/src/components/DcbTag.res +68 -0
- package/src/components/DcbTag.res.mjs +23 -0
- package/src/components/Plugin.res +22 -0
- package/src/components/Plugin.res.mjs +11 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
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.69 (2026-07-08)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **reventless-core:** classify plugins by kind in the lifecycle read model ([64e3f22](https://github.com/ReventlessDev/reventless-core/commit/64e3f22b8114a771886b7c8ec023e95971413c0b))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.68 (2026-07-07)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **reventless-aws:** thread inferred DCB scope into the deployed command handler ([4d8327f](https://github.com/ReventlessDev/reventless-core/commit/4d8327fad8659a1cde8c36098c72392737437af1))
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
# 3.0.0-alpha.67 (2026-07-06)
|
|
7
21
|
|
|
8
22
|
**Note:** Version bump only for package @reventlessdev/reventless-spec
|
package/package.json
CHANGED
|
@@ -989,6 +989,74 @@ let sliceShapeFromSchemas = (
|
|
|
989
989
|
}
|
|
990
990
|
}
|
|
991
991
|
|
|
992
|
+
/** One slice's sury schemas — the input to `deriveEffectiveScope`. */
|
|
993
|
+
type sliceSchemas = {
|
|
994
|
+
name: string,
|
|
995
|
+
commandSchema: S.t<unknown>,
|
|
996
|
+
consumedEventSchema: S.t<unknown>,
|
|
997
|
+
eventSchema: S.t<unknown>,
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
The DCB decision-read scope threaded into every StateChangeSlice callback:
|
|
1002
|
+
- `crossPartitionTagKeys` — keys whose scalar command tag must be fanned into its
|
|
1003
|
+
own single-tag clause (a cross-entity reference read), and
|
|
1004
|
+
- `tagKeysByEventType` — each produced event type's *indexed* tag keys, so a
|
|
1005
|
+
decision query drops vacuous (type, tag) clause combinations.
|
|
1006
|
+
*/
|
|
1007
|
+
type effectiveScope = {
|
|
1008
|
+
crossPartitionTagKeys: array<string>,
|
|
1009
|
+
tagKeysByEventType: dict<array<string>>,
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
Derives the effective DCB decision-read scope for one consistency boundary from
|
|
1014
|
+
its slices' schemas.
|
|
1015
|
+
|
|
1016
|
+
Prefers the slice-graph inference (`DcbScopeInference.infer`) and falls back to
|
|
1017
|
+
the `@crossPartition` / tag *annotations* only when a slice's partition is
|
|
1018
|
+
ambiguous — all-or-nothing, matching `Dcb_Builder`. This is the **single source of
|
|
1019
|
+
truth** shared by the deploy-time builder (`Dcb_Builder.res`) and the deployed
|
|
1020
|
+
command-handler entry point (`DcbCommandTopicEntryPoint.mjs`): both call this so
|
|
1021
|
+
the runtime decision query cannot diverge from the storage/GSI scope. Before this
|
|
1022
|
+
existed the entry point re-derived scope from annotations alone, silently dropping
|
|
1023
|
+
inferred cross-partition reference reads — see
|
|
1024
|
+
`docs/analysis/dcb-runtime-scope-annotation-drift.md`.
|
|
1025
|
+
*/
|
|
1026
|
+
let deriveEffectiveScope = (slices: array<sliceSchemas>): effectiveScope => {
|
|
1027
|
+
let producedSchemas = slices->Array.map(s => s.eventSchema)
|
|
1028
|
+
let annotatedCross = {
|
|
1029
|
+
let seen = Set.make()
|
|
1030
|
+
producedSchemas
|
|
1031
|
+
->Array.flatMap(extractCrossPartitionTagKeys)
|
|
1032
|
+
->Array.filter(k =>
|
|
1033
|
+
if seen->Set.has(k) {
|
|
1034
|
+
false
|
|
1035
|
+
} else {
|
|
1036
|
+
seen->Set.add(k)
|
|
1037
|
+
true
|
|
1038
|
+
}
|
|
1039
|
+
)
|
|
1040
|
+
}
|
|
1041
|
+
let annotatedTagKeys =
|
|
1042
|
+
producedSchemas->Array.map(extractTagKeysByEventType)->mergeTagKeysByEventType
|
|
1043
|
+
let shapes =
|
|
1044
|
+
slices->Array.map(s =>
|
|
1045
|
+
sliceShapeFromSchemas(
|
|
1046
|
+
~name=s.name,
|
|
1047
|
+
~commandSchema=s.commandSchema,
|
|
1048
|
+
~consumedEventSchema=s.consumedEventSchema,
|
|
1049
|
+
~eventSchema=s.eventSchema,
|
|
1050
|
+
)
|
|
1051
|
+
)
|
|
1052
|
+
let inferred = DcbScopeInference.infer(shapes)
|
|
1053
|
+
let useInferred = inferred.ambiguities->Array.length == 0
|
|
1054
|
+
{
|
|
1055
|
+
crossPartitionTagKeys: useInferred ? inferred.crossPartitionTagKeys : annotatedCross,
|
|
1056
|
+
tagKeysByEventType: useInferred ? inferred.tagKeysByEventType : annotatedTagKeys,
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
992
1060
|
/**
|
|
993
1061
|
Checks whether any single variant in a schema has multiple tagged fields.
|
|
994
1062
|
If so, a partition tag annotation is needed to disambiguate.
|
|
@@ -8,6 +8,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
8
8
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
9
9
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
10
10
|
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
11
|
+
import * as DcbScopeInference$Reventless from "./DcbScopeInference.res.mjs";
|
|
11
12
|
|
|
12
13
|
let tagSchema = S.schema(s => ({
|
|
13
14
|
key: s.m(S.string),
|
|
@@ -618,6 +619,27 @@ function sliceShapeFromSchemas(name, commandSchema, consumedEventSchema, eventSc
|
|
|
618
619
|
};
|
|
619
620
|
}
|
|
620
621
|
|
|
622
|
+
function deriveEffectiveScope(slices) {
|
|
623
|
+
let producedSchemas = slices.map(s => s.eventSchema);
|
|
624
|
+
let seen = new Set();
|
|
625
|
+
let annotatedCross = producedSchemas.flatMap(extractCrossPartitionTagKeys).filter(k => {
|
|
626
|
+
if (seen.has(k)) {
|
|
627
|
+
return false;
|
|
628
|
+
} else {
|
|
629
|
+
seen.add(k);
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
let annotatedTagKeys = mergeTagKeysByEventType(producedSchemas.map(extractTagKeysByEventType));
|
|
634
|
+
let shapes = slices.map(s => sliceShapeFromSchemas(s.name, s.commandSchema, s.consumedEventSchema, s.eventSchema));
|
|
635
|
+
let inferred = DcbScopeInference$Reventless.infer(shapes);
|
|
636
|
+
let useInferred = inferred.ambiguities.length === 0;
|
|
637
|
+
return {
|
|
638
|
+
crossPartitionTagKeys: useInferred ? inferred.crossPartitionTagKeys : annotatedCross,
|
|
639
|
+
tagKeysByEventType: useInferred ? inferred.tagKeysByEventType : annotatedTagKeys
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
621
643
|
function hasMultiTagVariant(schema) {
|
|
622
644
|
switch (schema.type) {
|
|
623
645
|
case "object" :
|
|
@@ -866,6 +888,7 @@ export {
|
|
|
866
888
|
eventShapesOfSchema,
|
|
867
889
|
extractPartitionTagFields,
|
|
868
890
|
sliceShapeFromSchemas,
|
|
891
|
+
deriveEffectiveScope,
|
|
869
892
|
hasMultiTagVariant,
|
|
870
893
|
findMultiTagVariantNames,
|
|
871
894
|
extractCompositePartitionFieldsFromProperties,
|
|
@@ -6,6 +6,23 @@ type name = string
|
|
|
6
6
|
@schema
|
|
7
7
|
type version = string
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
Classifies the business role of a plugin, carried on `pluginDefinition` so the
|
|
11
|
+
plugin-lifecycle read model can segregate infrastructure/commercial/marketplace
|
|
12
|
+
plugins from domain plugins in the admin Plugins view. Absent (on definitions
|
|
13
|
+
persisted before this field existed) is read as `Domain`.
|
|
14
|
+
|
|
15
|
+
Canonical here in `reventless-spec` because `pluginDefinition` (a `@schema` type
|
|
16
|
+
nested in the lifecycle Message union) needs the sury schema; `ReventlessCore.Plugin_BuiltHook`
|
|
17
|
+
re-exports this same type for its deploy-time metadata registry.
|
|
18
|
+
*/
|
|
19
|
+
@schema
|
|
20
|
+
type pluginKind =
|
|
21
|
+
| Domain
|
|
22
|
+
| PlatformInfrastructure
|
|
23
|
+
| Commercial
|
|
24
|
+
| Marketplace
|
|
25
|
+
|
|
9
26
|
/**
|
|
10
27
|
Describes an extension point exported by a plugin.
|
|
11
28
|
Included in the plugin's `pluginDefinition` for use by the gateway / host.
|
|
@@ -373,5 +390,10 @@ type pluginDefinition = {
|
|
|
373
390
|
// subscriptions from this plugin's DCB topic → peer EventCollectors.
|
|
374
391
|
// None for plugins without a DCB EventLog.
|
|
375
392
|
dcbEventLog: @s.matches(dcbEventLogOptionSchema) option<dcbEventLogDefinition>,
|
|
393
|
+
// Business role of this plugin. Mandatory: `Domain` is the default kind, resolved once
|
|
394
|
+
// at the deploy-metadata → definition boundary (Plugin_Builder). PlatformInfrastructure
|
|
395
|
+
// plugins are segregated out of the admin Plugins list. Payload-less variant → serialises
|
|
396
|
+
// as a bare JSON string, so it is JSON-safe inside the lifecycle Message union without js_nullable.
|
|
397
|
+
kind: pluginKind,
|
|
376
398
|
}
|
|
377
399
|
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
4
|
import * as SuryResMjs from "sury/src/Sury.res.mjs";
|
|
5
5
|
|
|
6
|
+
let pluginKindSchema = S.union([
|
|
7
|
+
S.literal("Domain"),
|
|
8
|
+
S.literal("PlatformInfrastructure"),
|
|
9
|
+
S.literal("Commercial"),
|
|
10
|
+
S.literal("Marketplace")
|
|
11
|
+
]);
|
|
12
|
+
|
|
6
13
|
let extensionPointDefinitionSchema = S.schema(s => ({
|
|
7
14
|
name: s.m(S.string),
|
|
8
15
|
commandTopic: s.m(S.string),
|
|
@@ -204,7 +211,8 @@ let pluginDefinitionSchema = S.schema(s => ({
|
|
|
204
211
|
apiTarget: s.m(stringOptionSchema),
|
|
205
212
|
uiFragments: s.m(uiFragmentManifestOptionSchema),
|
|
206
213
|
structure: s.m(pluginStructureOptionSchema),
|
|
207
|
-
dcbEventLog: s.m(dcbEventLogOptionSchema)
|
|
214
|
+
dcbEventLog: s.m(dcbEventLogOptionSchema),
|
|
215
|
+
kind: s.m(pluginKindSchema)
|
|
208
216
|
}));
|
|
209
217
|
|
|
210
218
|
let nameSchema = S.string;
|
|
@@ -214,6 +222,7 @@ let versionSchema = S.string;
|
|
|
214
222
|
export {
|
|
215
223
|
nameSchema,
|
|
216
224
|
versionSchema,
|
|
225
|
+
pluginKindSchema,
|
|
217
226
|
extensionPointDefinitionSchema,
|
|
218
227
|
extensionDefinitionSchema,
|
|
219
228
|
dcbEventLogDefinitionSchema,
|
|
@@ -248,4 +257,4 @@ export {
|
|
|
248
257
|
platformEventGraphSchema,
|
|
249
258
|
pluginDefinitionSchema,
|
|
250
259
|
}
|
|
251
|
-
/*
|
|
260
|
+
/* pluginKindSchema Not a pure module */
|