@reventlessdev/reventless-core 3.0.0-alpha.238 → 3.0.0-alpha.240
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 +40 -0
- package/package.json +12 -12
- package/src/Message.res +11 -11
- package/src/Message.res.mjs +15 -14
- package/src/Projection.res +2 -2
- package/src/Projection.res.mjs +5 -4
- package/src/adapter/Monitoring/Monitoring.res +24 -2
- package/src/adapter/Monitoring/Monitoring.res.mjs +3 -3
- package/src/admin/Platform_AdminApi.res.mjs +3 -3
- package/src/admin/Platform_Admin_Structure.res +33 -112
- package/src/admin/Platform_Admin_Structure.res.mjs +8 -138
- package/src/admin/Platform_ComponentDefinitionsApi.res +2 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res.mjs +12 -12
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments.res.mjs +14 -14
- package/src/components/Aggregate/Aggregate_Callback.res +2 -2
- package/src/components/Aggregate/Aggregate_Callback.res.mjs +4 -4
- package/src/components/Api/ApiAllowedStatesHelpers.res.mjs +3 -3
- package/src/components/Api/ApiNoApiHelpers.res.mjs +5 -5
- package/src/components/Api/ApiTargetStateHelpers.res.mjs +3 -3
- package/src/components/Api/GraphQL_FragmentGenerator.res +140 -24
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +48 -21
- package/src/components/Api/GraphQL_SchemaInspector.res +1 -1
- package/src/components/Api/GraphQL_SchemaInspector.res.mjs +1 -1
- package/src/components/Api/MCP_SchemaGenerator.res +1 -1
- package/src/components/Api/MCP_SchemaGenerator.res.mjs +1 -1
- package/src/components/Api/SchemaType.res +6 -10
- package/src/components/Api/SchemaType.res.mjs +4 -4
- package/src/components/Api/StorageRefFields.res +1 -1
- package/src/components/Api/StorageRefFields.res.mjs +1 -1
- package/src/components/Api/SuryToJsonSchema.res +19 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +10 -5
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -4
- package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +22 -23
- package/src/components/CommandTopic/CommandTopic_Callback.res +1 -2
- package/src/components/CommandTopic/CommandTopic_Callback.res.mjs +3 -4
- package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -1
- package/src/components/CommandTopic/CommandTopic_Helpers.res.mjs +3 -3
- package/src/components/Counter/Counter.res.mjs +2 -2
- package/src/components/Counter/Counter_Callback.res.mjs +4 -4
- package/src/components/Counter/Counter_Operations.res.mjs +4 -4
- package/src/components/Dcb/Dcb_Builder.res +1 -1
- package/src/components/Dcb/Dcb_Builder.res.mjs +8 -6
- package/src/components/DcbEventLog/DcbEventLog_Builder.res.mjs +4 -3
- package/src/components/EventCollector/EventCollector_Builder.res +1 -1
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +1 -2
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +11 -12
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res +3 -4
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs +24 -25
- package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +1 -1
- package/src/components/SideEffectHandler/SideEffectHandler_Callback.res.mjs +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +2 -2
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -4
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +3 -3
- package/src/plugin/api/PluginBaseFragment.res +12 -29
- package/src/plugin/api/PluginBaseFragment.res.mjs +3 -22
- package/src/plugin/component/Capability_Inference.res +118 -1
- package/src/plugin/component/Capability_Inference.res.mjs +128 -1
- package/src/plugin/component/Plugin_Builder.res +9 -2
- package/src/plugin/component/Plugin_Builder.res.mjs +5 -2
- package/src/plugin/component/Plugin_Helpers.res +11 -11
- package/src/plugin/component/Plugin_Helpers.res.mjs +11 -10
- package/src/plugin/component/Plugin_Structure.res +458 -296
- package/src/plugin/component/Plugin_Structure.res.mjs +259 -182
- package/src/plugin/component/Plugin_SubscriptionSchema.res +1 -1
- package/src/plugin/component/Plugin_SubscriptionSchema.res.mjs +1 -1
- package/src/plugin/component/SchemaWalker.res +11 -11
- package/src/plugin/component/SchemaWalker.res.mjs +8 -9
- package/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs +15 -15
- package/src/plugin/lifecycle/PluginBehavior.res.mjs +55 -55
- package/src/plugin/lifecycle/PluginSpec.res +22 -7
- package/src/plugin/lifecycle/PluginSpec.res.mjs +91 -36
- package/src/plugin/lifecycle/PluginsReadModelSpec.res +36 -9
- package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +33 -22
- package/src/util/Interstack.res +3 -3
- package/src/util/LogFormat.res +1 -2
- package/src/util/LogFormat.res.mjs +2 -2
- package/tests/IdentityTest.res +6 -7
- package/tests/IdentityTest.res.mjs +7 -9
- package/tests/adapter/MonitoringTest.res +30 -3
- package/tests/adapter/MonitoringTest.res.mjs +31 -12
- package/tests/adapter/RuntimeExtensionHookReachTest.res +0 -1
- package/tests/adapter/RuntimeExtensionHookReachTest.res.mjs +5 -7
- package/tests/admin/Platform_Admin_StructureTest.res +254 -0
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +219 -0
- package/tests/admin/Platform_BakedManifestTest.res +1 -0
- package/tests/admin/Platform_BakedManifestTest.res.mjs +1 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +3 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +6 -0
- package/tests/admin/Platform_PluginStructuresApiTest.res +1 -0
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +2 -0
- package/tests/aggregate/AggregateCacheTest.res +0 -1
- package/tests/aggregate/AggregateCacheTest.res.mjs +13 -15
- package/tests/aggregate/AggregateConflictTest.res +0 -1
- package/tests/aggregate/AggregateConflictTest.res.mjs +7 -9
- package/tests/aggregate/AggregateFixtures.res +0 -1
- package/tests/aggregate/AggregateFixtures.res.mjs +19 -21
- package/tests/aggregate/AggregateSnapshotTest.res +1 -2
- package/tests/aggregate/AggregateSnapshotTest.res.mjs +15 -16
- package/tests/api/ApiNoApiTest.res.mjs +17 -9
- package/tests/api/GraphQL_FragmentGeneratorTest.res +232 -0
- package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +207 -55
- package/tests/api/PsInternalFieldsReadModel.res +21 -0
- package/tests/api/PsInternalFieldsReadModel.res.mjs +74 -0
- package/tests/api/StorageRefFieldsTest.res.mjs +13 -13
- package/tests/api/SuryToJsonSchemaTest.res +73 -3
- package/tests/api/SuryToJsonSchemaTest.res.mjs +364 -816
- package/tests/commandgenerator/CommandGeneratorFixtures.res +0 -1
- package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +19 -21
- package/tests/commandgenerator/OwnerStampingTest.res +0 -1
- package/tests/commandgenerator/OwnerStampingTest.res.mjs +9 -11
- package/tests/commandtopic/CommandTopicCallbackFixtures.res +0 -1
- package/tests/commandtopic/CommandTopicCallbackFixtures.res.mjs +7 -9
- package/tests/counter/CounterFixtures.res +0 -1
- package/tests/counter/CounterFixtures.res.mjs +1 -4
- package/tests/dcb/DcbDecodeTest.res.mjs +24 -24
- package/tests/dcb/DcbEventLogOperationsTest.res +2 -2
- package/tests/dcb/DcbEventLogOperationsTest.res.mjs +3 -3
- package/tests/dcb/DcbEventLogSchemaTest.res.mjs +7 -7
- package/tests/dcb/DcbFixtures.res +0 -1
- package/tests/dcb/DcbFixtures.res.mjs +87 -89
- package/tests/dcb/DcbTagTest.res.mjs +6 -6
- package/tests/dcb/DcbValidationTest.res.mjs +52 -52
- package/tests/eventlog/EventLogFixtures.res +0 -1
- package/tests/eventlog/EventLogFixtures.res.mjs +7 -9
- package/tests/eventmapper/EventMapperFixtures.res +0 -1
- package/tests/eventmapper/EventMapperFixtures.res.mjs +24 -26
- package/tests/eventtopic/EventTopicFixtures.res +0 -1
- package/tests/eventtopic/EventTopicFixtures.res.mjs +7 -9
- package/tests/extensionpoint/ExtensionPointFixtures.res +0 -1
- package/tests/extensionpoint/ExtensionPointFixtures.res.mjs +10 -12
- package/tests/extensionpoint/ExtensionPointOperationsTest.res +1 -2
- package/tests/extensionpoint/ExtensionPointOperationsTest.res.mjs +9 -10
- package/tests/logger/LogFormatTest.res +3 -2
- package/tests/logger/LogFormatTest.res.mjs +7 -9
- package/tests/message/MessageTest.res +2 -2
- package/tests/message/MessageTest.res.mjs +8 -7
- package/tests/message/MetaEnvelopeTest.res +5 -5
- package/tests/message/MetaEnvelopeTest.res.mjs +12 -11
- package/tests/plugin/ManifestVisibilityTest.res.mjs +7 -7
- package/tests/plugin/PluginDefinitionScalars.res +5 -3
- package/tests/plugin/PluginDefinitionScalars.res.mjs +2 -2
- package/tests/plugin/PluginLifecycleCorpusTest.res +4 -3
- package/tests/plugin/PluginLifecycleCorpusTest.res.mjs +3 -6
- package/tests/plugin/PluginSpecExperiment.res +0 -1
- package/tests/plugin/PluginSpecExperiment.res.mjs +12 -14
- package/tests/plugin/PluginStructureTest.res +292 -2
- package/tests/plugin/PluginStructureTest.res.mjs +391 -88
- package/tests/plugin/Plugin_EventQuerySchemaTest.res.mjs +3 -3
- package/tests/plugin/SchemaWalkerTest.res +0 -1
- package/tests/plugin/SchemaWalkerTest.res.mjs +17 -19
- package/tests/plugin/StateChangeSlice/PsAttachInvoice.res.mjs +5 -5
- package/tests/plugin/StateChangeSlice/PsChangePhoto.res.mjs +8 -8
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res.mjs +5 -5
- package/tests/plugin/StateChangeSlice/PsGatedCommands.res.mjs +9 -9
- package/tests/plugin/StateChangeSlice/PsPlaceOrder.res.mjs +5 -5
- package/tests/plugin/StateChangeSlice/PsReserveStock.res.mjs +8 -8
- package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +15 -15
- package/tests/plugin/StateChangeSlice/PsTypoStore.res +31 -0
- package/tests/plugin/StateChangeSlice/PsTypoStore.res.mjs +62 -0
- package/tests/plugin/StateChangeSlice/PsUploadAvatar.res.mjs +8 -8
- package/tests/plugin/StateChangeSlice/PsUploadImages.res +40 -0
- package/tests/plugin/StateChangeSlice/PsUploadImages.res.mjs +66 -0
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +13 -12
- package/tests/plugin/StateViewSlice/PsAvailableProductsView.res.mjs +8 -8
- package/tests/plugin/StateViewSlice/PsCategoriesView.res.mjs +6 -6
- package/tests/plugin/StateViewSlice/PsCustomersView.res.mjs +10 -10
- package/tests/plugin/StateViewSlice/PsGatedView.res.mjs +6 -6
- package/tests/plugin/StateViewSlice/PsOrdersView.res.mjs +9 -9
- package/tests/plugin/StateViewSlice/PsShipmentsView.res.mjs +11 -10
- package/tests/plugin/pluginDefinitionRequiredScalars.txt +15 -0
- package/tests/querydb/QueryDbFixtures.res +0 -1
- package/tests/querydb/QueryDbFixtures.res.mjs +5 -7
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +0 -1
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +4 -6
- package/tests/util/CommandPublisherTest.res +0 -1
- package/tests/util/CommandPublisherTest.res.mjs +4 -6
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as
|
|
3
|
+
import * as Sury from "sury";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
let recordSchema = S.schema(s => ({
|
|
8
|
-
a: s.m(S.meta(S.string, {
|
|
5
|
+
let recordSchema = Sury.$schema(s => ({
|
|
6
|
+
a: s.m(Sury.meta(Sury.string, {
|
|
9
7
|
description: "aaa"
|
|
10
8
|
})),
|
|
11
|
-
b: s.m(
|
|
9
|
+
b: s.m(Sury.string)
|
|
12
10
|
}));
|
|
13
11
|
|
|
14
|
-
let variantSchema =
|
|
15
|
-
|
|
12
|
+
let variantSchema = Sury.union([
|
|
13
|
+
Sury.$schema(s => ({
|
|
16
14
|
TAG: "Q",
|
|
17
|
-
_0: s.m(
|
|
15
|
+
_0: s.m(Sury.int)
|
|
18
16
|
})),
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
Sury.literal("X"),
|
|
18
|
+
Sury.literal("Y")
|
|
21
19
|
]);
|
|
22
20
|
|
|
23
|
-
console.log("Record:", JSON.stringify(
|
|
21
|
+
console.log("Record:", JSON.stringify(Sury.toJSONSchema(recordSchema), undefined, 2));
|
|
24
22
|
|
|
25
|
-
console.log("Variant:", JSON.stringify(
|
|
23
|
+
console.log("Variant:", JSON.stringify(Sury.toJSONSchema(variantSchema), undefined, 2));
|
|
26
24
|
|
|
27
25
|
export {
|
|
28
26
|
recordSchema,
|
|
29
27
|
variantSchema,
|
|
30
28
|
}
|
|
31
|
-
/*
|
|
29
|
+
/* recordSchema Not a pure module */
|
|
@@ -116,6 +116,34 @@ module PsUploadAvatarSlice: ReventlessInfra.StateChangeSlice.T = {
|
|
|
116
116
|
let make = (~dcbEventLog as _, ~publishJsons as _, ~tagKeysByEventType as _=?, ~crossPartitionTagKeys as _=?, ~runtime as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
module PsUploadImagesSlice: ReventlessInfra.StateChangeSlice.T = {
|
|
120
|
+
module Spec = PsUploadImages
|
|
121
|
+
module Behavior = {
|
|
122
|
+
type state = PsUploadImages.state
|
|
123
|
+
let initialState = PsUploadImages.initialState
|
|
124
|
+
let evolve = PsUploadImages.evolve
|
|
125
|
+
let decide = PsUploadImages.decide
|
|
126
|
+
let moduleUrl = PsUploadImages.moduleUrl
|
|
127
|
+
}
|
|
128
|
+
let isAsync = false
|
|
129
|
+
type component = scsComponent
|
|
130
|
+
let make = (~dcbEventLog as _, ~publishJsons as _, ~tagKeysByEventType as _=?, ~crossPartitionTagKeys as _=?, ~runtime as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module PsTypoStoreSlice: ReventlessInfra.StateChangeSlice.T = {
|
|
134
|
+
module Spec = PsTypoStore
|
|
135
|
+
module Behavior = {
|
|
136
|
+
type state = PsTypoStore.state
|
|
137
|
+
let initialState = PsTypoStore.initialState
|
|
138
|
+
let evolve = PsTypoStore.evolve
|
|
139
|
+
let decide = PsTypoStore.decide
|
|
140
|
+
let moduleUrl = PsTypoStore.moduleUrl
|
|
141
|
+
}
|
|
142
|
+
let isAsync = false
|
|
143
|
+
type component = scsComponent
|
|
144
|
+
let make = (~dcbEventLog as _, ~publishJsons as _, ~tagKeysByEventType as _=?, ~crossPartitionTagKeys as _=?, ~runtime as _=?, ~opts as _=?): component => Obj.magic(0)
|
|
145
|
+
}
|
|
146
|
+
|
|
119
147
|
module PsChangePhotoSlice: ReventlessInfra.StateChangeSlice.T = {
|
|
120
148
|
module Spec = PsChangePhoto
|
|
121
149
|
module Behavior = {
|
|
@@ -277,7 +305,7 @@ describe("@transition cross-check", () => {
|
|
|
277
305
|
)->ignore
|
|
278
306
|
None
|
|
279
307
|
} catch {
|
|
280
|
-
|
|
|
308
|
+
| JsExn(e) => Some(JsExn.message(e)->Option.getOr(""))
|
|
281
309
|
| _ => Some("")
|
|
282
310
|
}
|
|
283
311
|
|
|
@@ -938,7 +966,7 @@ describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
938
966
|
groupBy: None,
|
|
939
967
|
visibility: None,
|
|
940
968
|
live: None,
|
|
941
|
-
retired: Some({field, label: "", showWhenFalse: false, values: None}),
|
|
969
|
+
retired: Some({field, label: "", showWhenFalse: false, values: None, namedWhenRetired: false}),
|
|
942
970
|
},
|
|
943
971
|
)
|
|
944
972
|
|
|
@@ -973,6 +1001,7 @@ describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
973
1001
|
label: "",
|
|
974
1002
|
showWhenFalse: false,
|
|
975
1003
|
values: Some(["Deactivated", "Closed"]),
|
|
1004
|
+
namedWhenRetired: false,
|
|
976
1005
|
}),
|
|
977
1006
|
},
|
|
978
1007
|
)
|
|
@@ -1017,6 +1046,108 @@ describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
1017
1046
|
})
|
|
1018
1047
|
})
|
|
1019
1048
|
|
|
1049
|
+
// A misspelled retirement state used to be fatal or merely logged depending on
|
|
1050
|
+
// where the enum happened to be declared — the PPX refuses it when the enum is
|
|
1051
|
+
// in the same file, and this pass, which sees the resolved schema, only warned.
|
|
1052
|
+
// File layout is not a defensible arbiter of whether a data-exposure bug stops
|
|
1053
|
+
// a build, so this rung was raised to match the other.
|
|
1054
|
+
describe("@retired state names are checked against the field's own enum", () => {
|
|
1055
|
+
let withRetiredStates = (schema, ~field, ~lifecycle, ~values) =>
|
|
1056
|
+
schema->S.castToUnknown->S.Metadata.set(
|
|
1057
|
+
~id=Reventless.StateAnnotations.stateAnnotationsId,
|
|
1058
|
+
{
|
|
1059
|
+
ids: [],
|
|
1060
|
+
compositeIds: [],
|
|
1061
|
+
subIds: [],
|
|
1062
|
+
compositeSubIds: [],
|
|
1063
|
+
indexes: [],
|
|
1064
|
+
hidden: [],
|
|
1065
|
+
summary: [],
|
|
1066
|
+
drillTargets: [],
|
|
1067
|
+
drillTargetKeys: [],
|
|
1068
|
+
collapsed: [],
|
|
1069
|
+
scan: [],
|
|
1070
|
+
scanSort: [],
|
|
1071
|
+
semantic: [],
|
|
1072
|
+
metric: [],
|
|
1073
|
+
lifecycle,
|
|
1074
|
+
groupBy: None,
|
|
1075
|
+
visibility: None,
|
|
1076
|
+
live: None,
|
|
1077
|
+
retired: Some({field, label: "", showWhenFalse: false, values, namedWhenRetired: false}),
|
|
1078
|
+
},
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
let shelf = values =>
|
|
1082
|
+
S.schema(s =>
|
|
1083
|
+
{
|
|
1084
|
+
"shelf": s.matches(S.union([S.literal("Listed"), S.literal("Archived")])),
|
|
1085
|
+
}
|
|
1086
|
+
)->withRetiredStates(~field="shelf", ~lifecycle=Some("shelf"), ~values=Some(values))
|
|
1087
|
+
|
|
1088
|
+
let check = schema => Plugin_Structure.checkRetiredValue(~entityName="Products", schema)
|
|
1089
|
+
|
|
1090
|
+
testSync("a declared state passes", () => {
|
|
1091
|
+
expect(check(shelf(["Archived"])))->toEqual(Plugin_Structure.Checked([]))
|
|
1092
|
+
})
|
|
1093
|
+
|
|
1094
|
+
// Reported per state rather than as a set: one wrong entry among several
|
|
1095
|
+
// still narrows something, so the symptom is a subset of rows leaking.
|
|
1096
|
+
testSync("each undeclared state is reported on its own", () => {
|
|
1097
|
+
switch check(shelf(["Archived", "Discontinud", "Withdrawn"])) {
|
|
1098
|
+
| Checked(failures) =>
|
|
1099
|
+
expect(failures->Array.length)->toBe(2)
|
|
1100
|
+
expect(failures->Array.some(f => f->String.includes("Discontinud")))->toBe(true)
|
|
1101
|
+
expect(failures->Array.some(f => f->String.includes("Withdrawn")))->toBe(true)
|
|
1102
|
+
// Naming what the field does declare is what turns the failure into a fix.
|
|
1103
|
+
expect(failures->Array.every(f => f->String.includes("Listed, Archived")))->toBe(true)
|
|
1104
|
+
| other => expect(other)->toEqual(Plugin_Structure.Checked([]))
|
|
1105
|
+
}
|
|
1106
|
+
})
|
|
1107
|
+
|
|
1108
|
+
// The condition that came with promoting this: a fatal rule that is silent
|
|
1109
|
+
// when it cannot run looks exactly like one that passed.
|
|
1110
|
+
testSync("a field with no cases to compare against says so instead of passing", () => {
|
|
1111
|
+
let schema =
|
|
1112
|
+
S.schema(s => {"shelf": s.matches(S.string)})->withRetiredStates(
|
|
1113
|
+
~field="shelf",
|
|
1114
|
+
~lifecycle=Some("shelf"),
|
|
1115
|
+
~values=Some(["Archived"]),
|
|
1116
|
+
)
|
|
1117
|
+
switch check(schema) {
|
|
1118
|
+
| Unchecked(why) => expect(why->String.includes("no cases"))->toBe(true)
|
|
1119
|
+
| other => expect(other)->toEqual(Plugin_Structure.Unchecked(""))
|
|
1120
|
+
}
|
|
1121
|
+
})
|
|
1122
|
+
|
|
1123
|
+
// The boolean form names no state, so there is nothing to compare — which is
|
|
1124
|
+
// not the same as having failed to compare it.
|
|
1125
|
+
testSync("the boolean form is not a skip", () => {
|
|
1126
|
+
let schema =
|
|
1127
|
+
S.schema(s => {"archived": s.matches(S.bool)})->withRetiredStates(
|
|
1128
|
+
~field="archived",
|
|
1129
|
+
~lifecycle=None,
|
|
1130
|
+
~values=None,
|
|
1131
|
+
)
|
|
1132
|
+
expect(Plugin_Structure.checkRetiredValue(~entityName="Products", schema))->toEqual(
|
|
1133
|
+
Plugin_Structure.NotDeclared,
|
|
1134
|
+
)
|
|
1135
|
+
})
|
|
1136
|
+
|
|
1137
|
+
// The second rule stays a warning: retirement still works, and which field
|
|
1138
|
+
// carries it is a modelling judgement rather than a name that is wrong.
|
|
1139
|
+
testSync("a state on a non-lifecycle field is not a failure", () => {
|
|
1140
|
+
let schema =
|
|
1141
|
+
S.schema(s =>
|
|
1142
|
+
{
|
|
1143
|
+
"shelf": s.matches(S.union([S.literal("Listed"), S.literal("Archived")])),
|
|
1144
|
+
"lifecycle": s.matches(S.union([S.literal("Draft"), S.literal("Live")])),
|
|
1145
|
+
}
|
|
1146
|
+
)->withRetiredStates(~field="shelf", ~lifecycle=Some("lifecycle"), ~values=Some(["Archived"]))
|
|
1147
|
+
expect(check(schema))->toEqual(Plugin_Structure.Checked([]))
|
|
1148
|
+
})
|
|
1149
|
+
})
|
|
1150
|
+
|
|
1020
1151
|
// Shared by the read-side and write-side schema cases below, which ask the
|
|
1021
1152
|
// same question of the same emitter.
|
|
1022
1153
|
let getProperty = (json: JSON.t, key: string): option<JSON.t> =>
|
|
@@ -1270,6 +1401,165 @@ describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
1270
1401
|
})
|
|
1271
1402
|
})
|
|
1272
1403
|
|
|
1404
|
+
// A field typed uploadable declares its store by being named. The collection
|
|
1405
|
+
// walk is unchanged: it matches the `StoredIn` payload, never the semantic id,
|
|
1406
|
+
// so a new id carrying one is provisioned by machinery that knows nothing
|
|
1407
|
+
// about it.
|
|
1408
|
+
describe("uploadable types derive their store from the field name", () => {
|
|
1409
|
+
let withUploads = Plugin_Structure.make(
|
|
1410
|
+
~name="TestPlugin",
|
|
1411
|
+
~stateChangeSlices=[module(PsUploadImagesSlice)],
|
|
1412
|
+
)
|
|
1413
|
+
let stores = withUploads.requiredStores->Option.getOr([])
|
|
1414
|
+
|
|
1415
|
+
testSync("the derived store is the field name, pluralised", () =>
|
|
1416
|
+
expect(stores->Array.includes("TestPlugin.productImages"))->toBe(true)
|
|
1417
|
+
)
|
|
1418
|
+
|
|
1419
|
+
testSync("an optional field still declares its store", () =>
|
|
1420
|
+
expect(stores->Array.includes("TestPlugin.categoryImages"))->toBe(true)
|
|
1421
|
+
)
|
|
1422
|
+
|
|
1423
|
+
// The array marker lives on the element type. Reading the field schema
|
|
1424
|
+
// directly answers `None`, which is the same silence as never declaring —
|
|
1425
|
+
// so this asserts collection, not just that the fixture compiled.
|
|
1426
|
+
testSync("an array field declares through its element", () =>
|
|
1427
|
+
expect(stores->Array.includes("TestPlugin.datasheets"))->toBe(true)
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
testSync("an explicit @storageRef overrides the derived name", () =>
|
|
1431
|
+
expect(stores->Array.includes("branding.logos"))->toBe(true)
|
|
1432
|
+
)
|
|
1433
|
+
|
|
1434
|
+
testSync("nothing else is collected", () => expect(stores->Array.length)->toBe(4))
|
|
1435
|
+
|
|
1436
|
+
// The event field derives the same store as the command field of the same
|
|
1437
|
+
// name, so the two collapse to one declaration site — the behaviour the
|
|
1438
|
+
// annotation form already had, reached without an annotation.
|
|
1439
|
+
testSync("each requirement keeps its declaring sites", () => {
|
|
1440
|
+
let expected: array<Reventless.Plugin.requiredStoreDeclaration> = [
|
|
1441
|
+
{
|
|
1442
|
+
store: "TestPlugin.categoryImages",
|
|
1443
|
+
component: "UploadImages",
|
|
1444
|
+
field: "categoryImage",
|
|
1445
|
+
annotation: Some("categoryImages"),
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
store: "TestPlugin.datasheets",
|
|
1449
|
+
component: "UploadImages",
|
|
1450
|
+
field: "datasheets",
|
|
1451
|
+
annotation: Some("datasheets"),
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
store: "TestPlugin.productImages",
|
|
1455
|
+
component: "UploadImages",
|
|
1456
|
+
field: "productImage",
|
|
1457
|
+
annotation: Some("productImages"),
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
store: "branding.logos",
|
|
1461
|
+
component: "UploadImages",
|
|
1462
|
+
field: "logo",
|
|
1463
|
+
annotation: Some("branding.logos"),
|
|
1464
|
+
},
|
|
1465
|
+
]
|
|
1466
|
+
expect(withUploads.requiredStoreDeclarations)->toEqual(Some(expected))
|
|
1467
|
+
})
|
|
1468
|
+
|
|
1469
|
+
let commandJson =
|
|
1470
|
+
((withUploads.stateChangeSlices->Array.getUnsafe(0)).commands->Array.getUnsafe(0)).schema
|
|
1471
|
+
->JSON.parseOrThrow
|
|
1472
|
+
|
|
1473
|
+
let semanticOf = field =>
|
|
1474
|
+
commandJson
|
|
1475
|
+
->getPropertyOf(field)
|
|
1476
|
+
->Option.flatMap(s => getProperty(s, "x-reventless-semantic"))
|
|
1477
|
+
->Option.flatMap(JSON.Decode.string)
|
|
1478
|
+
|
|
1479
|
+
// The whole point of the family: one declaration states the content type
|
|
1480
|
+
// too, so the UI stops learning "this is an image" from the field's name.
|
|
1481
|
+
testSync("the wire carries the content half, not just the store", () =>
|
|
1482
|
+
expect(semanticOf("productImage"))->toBe(Some("uploadableImage"))
|
|
1483
|
+
)
|
|
1484
|
+
|
|
1485
|
+
// The override replaces the *store*, not the type's semantic: a field
|
|
1486
|
+
// pointing at another plugin's store is still an image.
|
|
1487
|
+
testSync("an overridden field keeps its uploadable semantic", () =>
|
|
1488
|
+
expect(semanticOf("logo"))->toBe(Some("uploadableImage"))
|
|
1489
|
+
)
|
|
1490
|
+
|
|
1491
|
+
testSync("the non-image arm carries its own id, on the element", () =>
|
|
1492
|
+
expect(
|
|
1493
|
+
commandJson
|
|
1494
|
+
->getPropertyOf("datasheets")
|
|
1495
|
+
->Option.flatMap(s => getProperty(s, "items"))
|
|
1496
|
+
->Option.flatMap(s => getProperty(s, "x-reventless-semantic"))
|
|
1497
|
+
->Option.flatMap(JSON.Decode.string),
|
|
1498
|
+
)->toBe(Some("uploadableFile"))
|
|
1499
|
+
)
|
|
1500
|
+
})
|
|
1501
|
+
|
|
1502
|
+
// Deriving the store from the field name turns a typo into a second store
|
|
1503
|
+
// rather than into a compile error, so the plugin has to refuse it.
|
|
1504
|
+
describe("near-duplicate stores are refused", () => {
|
|
1505
|
+
let failureText = () =>
|
|
1506
|
+
try {
|
|
1507
|
+
Plugin_Structure.make(
|
|
1508
|
+
~name="TestPlugin",
|
|
1509
|
+
~stateChangeSlices=[module(PsTypoStoreSlice)],
|
|
1510
|
+
)->ignore
|
|
1511
|
+
""
|
|
1512
|
+
} catch {
|
|
1513
|
+
| JsExn(e) => JsExn.message(e)->Option.getOr("(no message)")
|
|
1514
|
+
| _ => "(not a JS error)"
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
testSync("two stores one transposition apart fail the build", () =>
|
|
1518
|
+
expect(failureText() != "")->toBe(true)
|
|
1519
|
+
)
|
|
1520
|
+
|
|
1521
|
+
testSync("the failure names both stores and both fields", () => {
|
|
1522
|
+
let text = failureText()
|
|
1523
|
+
expect([
|
|
1524
|
+
text->String.includes("TestPlugin.productImages"),
|
|
1525
|
+
text->String.includes("TestPlugin.productImgaes"),
|
|
1526
|
+
text->String.includes("TypoStore.productImage"),
|
|
1527
|
+
text->String.includes("@storageRef"),
|
|
1528
|
+
])->toEqual([true, true, true, true])
|
|
1529
|
+
})
|
|
1530
|
+
|
|
1531
|
+
// Every legitimate pair in the other fixtures has to stay legitimate — a
|
|
1532
|
+
// check that fails on real designs is worse than the typo it catches.
|
|
1533
|
+
testSync("the four stores of the uploadable fixture do not collide", () =>
|
|
1534
|
+
expect(
|
|
1535
|
+
Capability_Inference.collisions(
|
|
1536
|
+
Plugin_Structure.make(
|
|
1537
|
+
~name="TestPlugin",
|
|
1538
|
+
~stateChangeSlices=[module(PsUploadImagesSlice)],
|
|
1539
|
+
).requiredStoreDeclarations->Option.getOr([]),
|
|
1540
|
+
),
|
|
1541
|
+
)->toEqual([])
|
|
1542
|
+
)
|
|
1543
|
+
|
|
1544
|
+
testSync("one edit, transpositions included; two edits not", () =>
|
|
1545
|
+
expect(
|
|
1546
|
+
[
|
|
1547
|
+
("productImages", "productImgaes"),
|
|
1548
|
+
("productImages", "productImage"),
|
|
1549
|
+
("logos", "logo"),
|
|
1550
|
+
("documents", "logos"),
|
|
1551
|
+
("productImages", "productImgeas"),
|
|
1552
|
+
]->Array.map(((a, b)) => Capability_Inference.withinOneEdit(a, b)),
|
|
1553
|
+
)->toEqual([true, true, true, false, false])
|
|
1554
|
+
)
|
|
1555
|
+
|
|
1556
|
+
// Reached only through an explicit override: two *derived* names cannot
|
|
1557
|
+
// differ by number, because each is pluralised through its singular stem.
|
|
1558
|
+
testSync("a singular/plural pair collides even at two edits apart", () =>
|
|
1559
|
+
expect(Capability_Inference.sameStem("productImage", "productImages"))->toBe(true)
|
|
1560
|
+
)
|
|
1561
|
+
})
|
|
1562
|
+
|
|
1273
1563
|
// Making a field optional says the value may be absent. It does not say the
|
|
1274
1564
|
// field stopped being a storage ref or a reference — but both walks read the
|
|
1275
1565
|
// marker through `Semantic.get`, and an optional field's marker sits on the
|