@reventlessdev/reventless-core 3.0.0-alpha.260 → 3.0.0-alpha.261
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 +8 -0
- package/package.json +9 -9
- package/src/Behavior.res +1 -4
- package/src/Message.res +17 -14
- package/src/Projection.res +51 -25
- package/src/RequestContext.res +8 -9
- package/src/ResourceAttribution.res +28 -14
- package/src/adapter/Adapter.res +48 -4
- package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +5 -5
- package/src/adapter/Messaging/Messaging_Log_Backend.res +10 -7
- package/src/adapter/Messaging/Messaging_Log_Backend.res.mjs +2 -4
- package/src/adapter/Monitoring/Monitoring.res +16 -17
- package/src/adapter/QueryInterception/QueryInterception.res +0 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +12 -5
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +11 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res +1 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Single.res +1 -7
- package/src/adapter/RuntimeExtension/RuntimeExtension.res +0 -1
- package/src/admin/Platform_Admin.res +17 -6
- package/src/admin/Platform_AdminApi.res +14 -9
- package/src/admin/Platform_Admin_Callback.res +1 -1
- package/src/admin/Platform_BakedManifest.res +34 -34
- package/src/admin/Platform_ComponentDefinitionsApi.res +9 -21
- package/src/admin/Platform_PluginStructuresApi.res +15 -12
- package/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry.res +1 -1
- package/src/components/Aggregate/Aggregate.res +13 -12
- package/src/components/Aggregate/Aggregate_Builder.res +22 -21
- package/src/components/Aggregate/Aggregate_Callback.res +91 -86
- package/src/components/Api/ApiNoApiHelpers.res +1 -3
- package/src/components/Api/Api_Ids.res +0 -1
- package/src/components/Api/Api_Naming.res +41 -18
- package/src/components/Api/GraphQL_FragmentGenerator.res +38 -51
- package/src/components/Api/GraphQL_SchemaInspector.res +5 -3
- package/src/components/Api/GraphQL_Stitcher.res +9 -10
- package/src/components/Api/MCP_SchemaGenerator.res +8 -12
- package/src/components/Api/OwnerScopeDiagnostics.res +1 -2
- package/src/components/Api/QueryDbListQuery.res +13 -6
- package/src/components/Api/SchemaType.res +28 -23
- package/src/components/Api/SuryToJsonSchema.res +9 -10
- package/src/components/AutomationSlice/AutomationSlice.res +4 -4
- package/src/components/AutomationSlice/AutomationSlice_Builder.res +15 -14
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -3
- package/src/components/Builder_Helpers.res +12 -23
- package/src/components/CommandGenerator/CommandGenerator.res +6 -1
- package/src/components/CommandGenerator/CommandGenerator_Builder.res +29 -21
- package/src/components/CommandGenerator/CommandGenerator_Callback.res +17 -21
- package/src/components/CommandTopic/CommandTopic.res +3 -3
- package/src/components/CommandTopic/CommandTopic_Builder.res +10 -10
- package/src/components/CommandTopic/CommandTopic_Callback.res +1 -4
- package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -2
- package/src/components/Counter/Counter_Builder.res +1 -6
- package/src/components/Counter/Counter_Callback.res +3 -3
- package/src/components/Dcb/Dcb_Builder.res +970 -956
- package/src/components/DcbEventLog/DcbEventLog.res +4 -4
- package/src/components/DcbEventLog/DcbEventLog_Builder.res +0 -1
- package/src/components/DcbEventLog/DcbEventLog_Operations.res +17 -18
- package/src/components/EventCollector/EventCollector_Builder.res +2 -1
- package/src/components/EventLog/EventLog.res +5 -1
- package/src/components/EventLog/EventLog_Operations.res +12 -8
- package/src/components/EventMapper/EventMapper.res +8 -10
- package/src/components/EventMapper/EventMapper_Builder.res +4 -1
- package/src/components/EventMapper/EventMapper_Callback.res +46 -31
- package/src/components/EventTopic/EventTopic.res +3 -3
- package/src/components/EventTopic/EventTopic_Builder.res +1 -1
- package/src/components/Extension/Extension.res +4 -1
- package/src/components/Extension/Extension_Operations.res +11 -3
- package/src/components/ExtensionPoint/ExtensionPoint.res +3 -3
- package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +9 -10
- package/src/components/ExtensionPoint/ExtensionPoint_Callback.res +2 -6
- package/src/components/ExtensionPoint/ExtensionPoint_Operations.res +13 -5
- package/src/components/Heartbeat/Heartbeat_Callback.res +3 -3
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +4 -4
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +7 -10
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res +4 -4
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +15 -14
- package/src/components/QueryDb/QueryDb_Adapter.res +1 -3
- package/src/components/QueryDb/QueryDb_Operations.res +10 -10
- package/src/components/ReadModel/ReadModel.res +3 -3
- package/src/components/ReadModel/ReadModel_Builder.res +1 -2
- package/src/components/ReadModel/ReadModel_Callback.res +2 -1
- package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +5 -2
- package/src/components/StateChangeSlice/StateChangeSlice.res +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +23 -4
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +22 -6
- package/src/components/StateTopic.res +1 -3
- package/src/components/StateViewSlice/StateViewSlice.res +4 -4
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +79 -68
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +3 -1
- package/src/components/Task/Task.res +3 -3
- package/src/components/Task/Task_Adapter.res +4 -1
- package/src/components/Task/Task_Builder.res +24 -21
- package/src/plugin/api/PluginBaseFragment.res +9 -3
- package/src/plugin/component/Capability_Inference.res +8 -10
- package/src/plugin/component/Plugin.res +6 -6
- package/src/plugin/component/Plugin_Builder.res +54 -50
- package/src/plugin/component/Plugin_Callback.res +10 -6
- package/src/plugin/component/Plugin_Helpers.res +264 -230
- package/src/plugin/component/Plugin_Structure.res +370 -324
- package/src/plugin/component/Plugin_SubscriptionSchema.res +8 -6
- package/src/plugin/component/SchemaWalker.res +11 -12
- package/src/plugin/connect/PluginConnectExtension_Mapping.res +45 -50
- package/src/plugin/lifecycle/PluginBehavior.res +3 -4
- package/src/plugin/lifecycle/PluginSpec.res +1 -2
- package/src/plugin/lifecycle/PluginsProjection.res +16 -5
- package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
- package/src/util/CommandPublisher.res +9 -2
- package/src/util/CsvStream.res +3 -3
- package/src/util/Interstack.res +7 -15
- package/src/util/Interstack.resi +3 -3
- package/src/util/LogFormat.res +5 -9
- package/src/util/Logger.res +1 -2
- package/src/util/Lru.res +2 -1
- package/src/util/Lru.resi +2 -3
- package/src/util/ScheduleOps.res +24 -27
- package/src/util/StorePrefixCollision.res +6 -9
- package/src/util/Util_Promise.res +4 -1
- package/src/util/Util_Promise.res.mjs +1 -1
- package/tests/IdentityTest.res +166 -113
- package/tests/ResourceAttributionTest.res +28 -25
- package/tests/StorePrefixCollisionTest.res +2 -1
- package/tests/TaskBucketNamingTest.res +3 -2
- package/tests/adapter/EventLogProvisioningTest.res +51 -48
- package/tests/adapter/MonitoringTest.res +81 -38
- package/tests/adapter/QueryInterceptionTest.res +2 -2
- package/tests/adapter/RefusalVocabularyTest.res +31 -23
- package/tests/adapter/RuntimeExtensionHookReachTest.res +9 -8
- package/tests/adapter/RuntimeExtensionTest.res +5 -3
- package/tests/admin/AdminApiSchemaDriftTest.res +22 -16
- package/tests/admin/Platform_Admin_StructureTest.res +10 -11
- package/tests/admin/Platform_BakedManifestTest.res +31 -35
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +18 -15
- package/tests/admin/Platform_PluginStructuresApiTest.res +5 -3
- package/tests/admin/Platform_UIFragmentsApiTest.res +6 -3
- package/tests/aggregate/AggregateCacheTest.res +10 -12
- package/tests/aggregate/AggregateCallbackTest.res +75 -46
- package/tests/aggregate/AggregateCausationTest.res +19 -20
- package/tests/aggregate/AggregateConflictTest.res +41 -19
- package/tests/aggregate/AggregateFixtures.res +15 -5
- package/tests/aggregate/AggregateRejectionTest.res +97 -80
- package/tests/aggregate/AggregateSnapshotTest.res +44 -16
- package/tests/api/Api_IdsTest.res +10 -4
- package/tests/api/BrandedMarkerCompositionTest.res +48 -36
- package/tests/api/GeolocationSemanticTest.res +3 -3
- package/tests/api/GraphQL_FragmentGeneratorTest.res +66 -54
- package/tests/api/OwnerScopeDiagnosticsTest.res +31 -27
- package/tests/api/StorageRefFieldsTest.res +30 -18
- package/tests/api/SuryToJsonSchemaTest.res +1315 -1014
- package/tests/api/TaggedUnionSchemaTest.res +17 -24
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res +235 -179
- package/tests/commandgenerator/CommandGeneratorFixtures.res +8 -13
- package/tests/commandgenerator/OwnerStampingTest.res +91 -76
- package/tests/commandtopic/CommandOutcomeHookTest.res +198 -155
- package/tests/commandtopic/CommandTopicCallbackFixtures.res +5 -4
- package/tests/commandtopic/CommandTopicCallbackTest.res +61 -43
- package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +70 -55
- package/tests/counter/CounterCallbackTest.res +40 -34
- package/tests/counter/CounterFixtures.res +4 -4
- package/tests/dcb/DcbDecodeTest.res +115 -84
- package/tests/dcb/DcbEventLogOperationsTest.res +194 -143
- package/tests/dcb/DcbEventLogSchemaTest.res +13 -13
- package/tests/dcb/DcbFixtures.res +4 -5
- package/tests/dcb/DcbScopeInferenceTest.res +173 -114
- package/tests/dcb/DcbStateChangeSliceTest.res +391 -262
- package/tests/dcb/DcbTagTest.res +22 -11
- package/tests/dcb/DcbValidationTest.res +288 -218
- package/tests/eventlog/EventLogFixtures.res +4 -4
- package/tests/eventlog/EventLogOperationsTest.res +102 -67
- package/tests/eventlog/EventLogRetryTest.res +104 -65
- package/tests/eventmapper/EventMapperCallbackTest.res +118 -83
- package/tests/eventmapper/EventMapperFixtures.res +15 -23
- package/tests/eventmapper/EventMapperTest.res +54 -38
- package/tests/eventtopic/EventTopicFixtures.res +3 -4
- package/tests/eventtopic/EventTopicOperationsTest.res +41 -29
- package/tests/extensionpoint/ExtensionPointCallbackTest.res +58 -45
- package/tests/extensionpoint/ExtensionPointFixtures.res +16 -20
- package/tests/extensionpoint/ExtensionPointOperationsTest.res +70 -49
- package/tests/extensionpoint/PluginDisconnectScheduleTest.res +2 -1
- package/tests/logger/LogFormatTest.res +104 -82
- package/tests/logger/LoggerLazyTest.res +21 -12
- package/tests/message/MessageTest.res +96 -93
- package/tests/message/MetaEnvelopeTest.res +26 -30
- package/tests/plugin/HeartbeatDisconnectGraceTest.res +31 -25
- package/tests/plugin/ManifestVisibilityTest.res +20 -12
- package/tests/plugin/PluginDefinitionScalars.res +1 -2
- package/tests/plugin/PluginLifecycleCorpusTest.res +15 -12
- package/tests/plugin/PluginSpecExperiment.res +0 -1
- package/tests/plugin/PluginStructureTest.res +1529 -1045
- package/tests/plugin/Plugin_EventQuerySchemaTest.res +21 -10
- package/tests/plugin/SchemaWalkerTest.res +116 -70
- package/tests/plugin/StateChange/PsReserveStock.res +4 -4
- package/tests/plugin/StateView/PsAnnotatedView.res +4 -4
- package/tests/plugin/StateView/PsCustomersView.res +4 -4
- package/tests/plugin/StateView/PsShipmentsView.res +3 -2
- package/tests/projection/LifecycleTrailTest.res +1 -4
- package/tests/projection/ProjectionOptimizeTest.res +13 -9
- package/tests/querydb/QueryDbFixtures.res +0 -1
- package/tests/querydb/QueryDbOperationsTest.res +99 -64
- package/tests/querydb/TaggedUnionStampTest.res +10 -8
- package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res +50 -35
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +4 -10
- package/tests/util/CommandPublisherTest.res +0 -1
- package/tests/util/CsvStreamTest.res +3 -3
- package/tests/util/LruTest.res +84 -56
|
@@ -19,8 +19,14 @@ describe("Monitoring", () => {
|
|
|
19
19
|
testSync("an announcement made before any backend is registered reaches the first one", () => {
|
|
20
20
|
let recorded: array<(Monitoring.unitKind, string, option<string>)> = []
|
|
21
21
|
module Late: Monitoring.Backend = {
|
|
22
|
-
let onProvisioned = (
|
|
23
|
-
|
|
22
|
+
let onProvisioned = (
|
|
23
|
+
~kind,
|
|
24
|
+
~name,
|
|
25
|
+
~component as _,
|
|
26
|
+
~plugin,
|
|
27
|
+
~platform as _,
|
|
28
|
+
~logLocator as _,
|
|
29
|
+
) => recorded->Array.push((kind, name, plugin))
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
// Provisioned at import time, inside a construct scope that is long gone by the time
|
|
@@ -42,8 +48,14 @@ describe("Monitoring", () => {
|
|
|
42
48
|
// Drained: a second registration does not receive them again.
|
|
43
49
|
let second: array<string> = []
|
|
44
50
|
module Other: Monitoring.Backend = {
|
|
45
|
-
let onProvisioned = (
|
|
46
|
-
|
|
51
|
+
let onProvisioned = (
|
|
52
|
+
~kind as _,
|
|
53
|
+
~name,
|
|
54
|
+
~component as _,
|
|
55
|
+
~plugin as _,
|
|
56
|
+
~platform as _,
|
|
57
|
+
~logLocator as _,
|
|
58
|
+
) => second->Array.push(name)
|
|
47
59
|
}
|
|
48
60
|
Monitoring.use(module(Other: Monitoring.Backend))
|
|
49
61
|
expect(second)->toEqual([])
|
|
@@ -54,12 +66,22 @@ describe("Monitoring", () => {
|
|
|
54
66
|
testSync("use registers a backend that receives every notify, role and name", () => {
|
|
55
67
|
let recorded: array<(Monitoring.unitKind, string)> = []
|
|
56
68
|
module Recorder: Monitoring.Backend = {
|
|
57
|
-
let onProvisioned = (
|
|
58
|
-
|
|
69
|
+
let onProvisioned = (
|
|
70
|
+
~kind,
|
|
71
|
+
~name,
|
|
72
|
+
~component as _,
|
|
73
|
+
~plugin as _,
|
|
74
|
+
~platform as _,
|
|
75
|
+
~logLocator as _,
|
|
76
|
+
) => recorded->Array.push((kind, name))
|
|
59
77
|
}
|
|
60
78
|
Monitoring.use(module(Recorder: Monitoring.Backend))
|
|
61
79
|
|
|
62
|
-
Monitoring.notify(
|
|
80
|
+
Monitoring.notify(
|
|
81
|
+
~kind=CommandHandler,
|
|
82
|
+
~name="AllAggregatesCmdHandler",
|
|
83
|
+
~component=stubResource,
|
|
84
|
+
)
|
|
63
85
|
Monitoring.notify(~kind=Projection, ~name="AllStateViewSlices", ~component=stubResource)
|
|
64
86
|
Monitoring.notify(~kind=DeadLetterSink, ~name="DeadLetterQueue", ~component=stubResource)
|
|
65
87
|
Monitoring.notify(~kind=Other("Counter"), ~name="ProductCounter", ~component=stubResource)
|
|
@@ -78,8 +100,14 @@ describe("Monitoring", () => {
|
|
|
78
100
|
testSync("notify forwards the static name and component resource to the backend", () => {
|
|
79
101
|
let seen: ref<option<(string, ReventlessInfra.Adapter.resource)>> = ref(None)
|
|
80
102
|
module Capture: Monitoring.Backend = {
|
|
81
|
-
let onProvisioned = (
|
|
82
|
-
|
|
103
|
+
let onProvisioned = (
|
|
104
|
+
~kind as _,
|
|
105
|
+
~name,
|
|
106
|
+
~component,
|
|
107
|
+
~plugin as _,
|
|
108
|
+
~platform as _,
|
|
109
|
+
~logLocator as _,
|
|
110
|
+
) => seen := Some((name, component))
|
|
83
111
|
}
|
|
84
112
|
Monitoring.use(module(Capture: Monitoring.Backend))
|
|
85
113
|
|
|
@@ -92,33 +120,42 @@ describe("Monitoring", () => {
|
|
|
92
120
|
Monitoring.reset()
|
|
93
121
|
})
|
|
94
122
|
|
|
95
|
-
testSync(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
let
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
(
|
|
116
|
-
(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
testSync(
|
|
124
|
+
"notify delivers the ambient plugin/platform inside a construct scope, None outside",
|
|
125
|
+
() => {
|
|
126
|
+
let seen: array<(option<string>, option<string>)> = []
|
|
127
|
+
module OwnerCapture: Monitoring.Backend = {
|
|
128
|
+
let onProvisioned = (
|
|
129
|
+
~kind as _,
|
|
130
|
+
~name as _,
|
|
131
|
+
~component as _,
|
|
132
|
+
~plugin,
|
|
133
|
+
~platform,
|
|
134
|
+
~logLocator as _,
|
|
135
|
+
) => seen->Array.push((plugin, platform))
|
|
136
|
+
}
|
|
137
|
+
Monitoring.use(module(OwnerCapture: Monitoring.Backend))
|
|
138
|
+
|
|
139
|
+
// Outside any plugin construct: platform substrate → both None.
|
|
140
|
+
Monitoring.notify(~kind=CommandHandler, ~name="ApiRouter", ~component=stubResource)
|
|
141
|
+
|
|
142
|
+
// Inside a construct scope (what Plugin_Builder.construct establishes): both delivered.
|
|
143
|
+
let prev = ResourceAttribution.enter(~platform="online-shop", ~plugin="Ordering")
|
|
144
|
+
Monitoring.notify(
|
|
145
|
+
~kind=CommandHandler,
|
|
146
|
+
~name="AllAggregatesCmdHandler",
|
|
147
|
+
~component=stubResource,
|
|
148
|
+
)
|
|
149
|
+
ResourceAttribution.restore(prev)
|
|
150
|
+
|
|
151
|
+
// After restore: back to None.
|
|
152
|
+
Monitoring.notify(~kind=Projection, ~name="AllStateViewSlices", ~component=stubResource)
|
|
153
|
+
|
|
154
|
+
expect(seen)->toEqual([(None, None), (Some("Ordering"), Some("online-shop")), (None, None)])
|
|
155
|
+
|
|
156
|
+
Monitoring.reset()
|
|
157
|
+
},
|
|
158
|
+
)
|
|
122
159
|
|
|
123
160
|
// Unlike the owner, the locator is not ambient — the seam only forwards what the provisioning
|
|
124
161
|
// site passed. A site with nothing to say omits it, and the backend must see that as "this unit
|
|
@@ -129,8 +166,14 @@ describe("Monitoring", () => {
|
|
|
129
166
|
let stubLocator: Pulumi.Output.t<string> = %raw(`{}`)
|
|
130
167
|
let seen: array<bool> = []
|
|
131
168
|
module LocatorCapture: Monitoring.Backend = {
|
|
132
|
-
let onProvisioned = (
|
|
133
|
-
|
|
169
|
+
let onProvisioned = (
|
|
170
|
+
~kind as _,
|
|
171
|
+
~name as _,
|
|
172
|
+
~component as _,
|
|
173
|
+
~plugin as _,
|
|
174
|
+
~platform as _,
|
|
175
|
+
~logLocator,
|
|
176
|
+
) => seen->Array.push(logLocator->Option.isSome)
|
|
134
177
|
}
|
|
135
178
|
Monitoring.use(module(LocatorCapture: Monitoring.Backend))
|
|
136
179
|
|
|
@@ -46,8 +46,8 @@ describe("QueryInterception", () => {
|
|
|
46
46
|
|
|
47
47
|
testSync("and registering a runtime interceptor does not switch provisioning on", () => {
|
|
48
48
|
Interception.reset()
|
|
49
|
-
QueryDb_Callback.registerQueryInterceptor(
|
|
50
|
-
Promise.resolve(QueryDb_Callback.Allow)
|
|
49
|
+
QueryDb_Callback.registerQueryInterceptor(
|
|
50
|
+
(~identity as _, ~readModelName as _, ~args as _) => Promise.resolve(QueryDb_Callback.Allow),
|
|
51
51
|
)
|
|
52
52
|
expect(Interception.isEnabled())->toBe(false)
|
|
53
53
|
QueryDb_Callback.clearQueryInterceptor()
|
|
@@ -17,39 +17,47 @@ describe("Auth_RefusalVocabulary — the table", () => {
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
testSync("each adapter can express both refusals", () => {
|
|
20
|
-
[Local, AppSync]->Array.forEach(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
[Local, AppSync]->Array.forEach(
|
|
21
|
+
adapter => {
|
|
22
|
+
let kinds = signalsFor(adapter)->Array.map(s => s.kind)
|
|
23
|
+
expect(kinds->Array.includes(Entitlement))->toBe(true)
|
|
24
|
+
expect(kinds->Array.includes(Identity))->toBe(true)
|
|
25
|
+
},
|
|
26
|
+
)
|
|
25
27
|
})
|
|
26
28
|
|
|
27
29
|
testSync("within an adapter the two refusals are actually distinguishable", () => {
|
|
28
30
|
// A mapping whose two rows look identical to a client would be worthless —
|
|
29
31
|
// it has to differ in the status, the place to look, or the value found.
|
|
30
|
-
[Local, AppSync]->Array.forEach(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
[Local, AppSync]->Array.forEach(
|
|
33
|
+
adapter => {
|
|
34
|
+
let signals = signalsFor(adapter)
|
|
35
|
+
let entitlement = signals->Array.find(s => s.kind == Entitlement)
|
|
36
|
+
let identity = signals->Array.find(s => s.kind == Identity)
|
|
37
|
+
switch (entitlement, identity) {
|
|
38
|
+
| (Some(e), Some(i)) =>
|
|
39
|
+
let differs =
|
|
40
|
+
e.httpStatus != i.httpStatus || e.discriminator != i.discriminator || e.value != i.value
|
|
41
|
+
expect(differs)->toBe(true)
|
|
42
|
+
| _ => JsError.throwWithMessage("adapter is missing one of the two refusals")
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
)
|
|
42
46
|
})
|
|
43
47
|
})
|
|
44
48
|
|
|
45
49
|
describe("Auth_RefusalVocabulary.classify", () => {
|
|
46
50
|
testSync("classifies every row of the table as that row's kind", () => {
|
|
47
|
-
signals->Array.forEach(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
signals->Array.forEach(
|
|
52
|
+
s => {
|
|
53
|
+
let errorType = s.discriminator->String.includes("errorType") ? Some(s.value) : None
|
|
54
|
+
let extensionsCode = s.discriminator == "extensions.code" ? Some(s.value) : None
|
|
55
|
+
// The 401 row's discriminator is a response header; status alone carries it.
|
|
56
|
+
expect(classify(~httpStatus=s.httpStatus, ~errorType?, ~extensionsCode?))->toEqual(
|
|
57
|
+
Some(s.kind),
|
|
58
|
+
)
|
|
59
|
+
},
|
|
60
|
+
)
|
|
53
61
|
})
|
|
54
62
|
|
|
55
63
|
// The trap the table exists to defuse: on AppSync the entitlement value is a
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
open JestGlobals
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
// The point of the seam, end to end at the hook level: an extension that only
|
|
5
4
|
// ever gets `onColdStart` can reach the framework's runtime callback hooks from
|
|
6
5
|
// there, and they take effect on the requests that follow.
|
|
@@ -49,13 +48,15 @@ describe("RuntimeExtension reaches the runtime callback hooks", () => {
|
|
|
49
48
|
let companionModuleUrls = []
|
|
50
49
|
let onColdStart = (~runtimeKind as _, ~component, ~plugin as _, ~platform as _) => {
|
|
51
50
|
seenAtColdStart->Array.push(component)
|
|
52
|
-
CommandGenerator_Callback.registerCommandInterceptor(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
CommandGenerator_Callback.registerCommandInterceptor(
|
|
52
|
+
async (
|
|
53
|
+
~identity as _,
|
|
54
|
+
~componentName,
|
|
55
|
+
~componentKind as _,
|
|
56
|
+
~tag,
|
|
57
|
+
~args as _,
|
|
58
|
+
) => CommandGenerator_Callback.Deny(`${componentName}.${tag} refused`),
|
|
59
|
+
)
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
RuntimeExtension.use(module(Gatekeeper: RuntimeExtension.Extension))
|
|
@@ -7,9 +7,11 @@ type call = {
|
|
|
7
7
|
platform: option<string>,
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
let recorderNamed = (
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
let recorderNamed = (
|
|
11
|
+
~companions: array<string>=[],
|
|
12
|
+
label: string,
|
|
13
|
+
into: array<(string, call)>,
|
|
14
|
+
): module(RuntimeExtension.Extension) => {
|
|
13
15
|
module R = {
|
|
14
16
|
let moduleUrl = `file:///pkg/${label}.res.mjs`
|
|
15
17
|
let companionModuleUrls = companions
|
|
@@ -442,25 +442,32 @@ guards->Array.forEach(g => {
|
|
|
442
442
|
describe(g.gqlType, () => {
|
|
443
443
|
// Without this, a renamed GraphQL type or an unreadable schema would make
|
|
444
444
|
// every assertion below pass over two empty lists.
|
|
445
|
-
testSync(
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
445
|
+
testSync(
|
|
446
|
+
"the SDL block and the record schema are both readable",
|
|
447
|
+
() => {
|
|
448
|
+
expect(sdlFields->Array.length > 0)->toEqual(true)
|
|
449
|
+
expect(g.recordFields->Array.length > 0)->toEqual(true)
|
|
450
|
+
},
|
|
451
|
+
)
|
|
449
452
|
|
|
450
|
-
testSync(
|
|
451
|
-
|
|
453
|
+
testSync(
|
|
454
|
+
"every encoded field is selectable",
|
|
455
|
+
() => expect(missing(g.wireFields, sdlFields))->toEqual([]),
|
|
452
456
|
)
|
|
453
457
|
|
|
454
|
-
testSync(
|
|
455
|
-
|
|
458
|
+
testSync(
|
|
459
|
+
"every declared field is encoded",
|
|
460
|
+
() => expect(missing(sdlFields, g.wireFields))->toEqual([]),
|
|
456
461
|
)
|
|
457
462
|
|
|
458
|
-
testSync(
|
|
459
|
-
|
|
463
|
+
testSync(
|
|
464
|
+
"every record field reaches the wire",
|
|
465
|
+
() => expect(missing(g.recordFields, Array.concat(sdlFields, g.recordOnly)))->toEqual([]),
|
|
460
466
|
)
|
|
461
467
|
|
|
462
|
-
testSync(
|
|
463
|
-
|
|
468
|
+
testSync(
|
|
469
|
+
"the wire carries nothing the record and the divergences do not name",
|
|
470
|
+
() => expect(missing(sdlFields, Array.concat(g.recordFields, g.wireOnly)))->toEqual([]),
|
|
464
471
|
)
|
|
465
472
|
})
|
|
466
473
|
})
|
|
@@ -479,10 +486,9 @@ describe("derivedPages", () => {
|
|
|
479
486
|
)
|
|
480
487
|
expect(jsonKeys(baked)->Array.includes("derivedPages"))->toEqual(true)
|
|
481
488
|
expect(
|
|
482
|
-
sdlFieldNames(
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
)->Array.includes("derivedPages"),
|
|
489
|
+
sdlFieldNames(~typeName="Platform_ComponentDefinitionEntry", componentSdl)->Array.includes(
|
|
490
|
+
"derivedPages",
|
|
491
|
+
),
|
|
486
492
|
)->toEqual(false)
|
|
487
493
|
})
|
|
488
494
|
})
|
|
@@ -44,8 +44,7 @@ describe("the Plugins read model declares its lifecycle", () => {
|
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
testSync("is served on the encoded def under the new key", () => {
|
|
47
|
-
let json =
|
|
48
|
-
rm->Platform_ComponentDefinitionsApi.encodeQueryableDef->JSON.stringify
|
|
47
|
+
let json = rm->Platform_ComponentDefinitionsApi.encodeQueryableDef->JSON.stringify
|
|
49
48
|
expect(json->String.includes(`"lifecycleField":"status"`))->toEqual(true)
|
|
50
49
|
})
|
|
51
50
|
|
|
@@ -137,11 +136,7 @@ describe("the Plugin command defs carry every argument the SDL requires", () =>
|
|
|
137
136
|
Platform_Admin_Structure.pluginCommands
|
|
138
137
|
->Array.filter(c => c.apiExposed != Some(false))
|
|
139
138
|
->Array.map(c => c.mutationField),
|
|
140
|
-
)->toEqual([
|
|
141
|
-
"Platform_Plugin_Activate",
|
|
142
|
-
"Platform_Plugin_Deactivate",
|
|
143
|
-
"Platform_Plugin_Retire",
|
|
144
|
-
])
|
|
139
|
+
)->toEqual(["Platform_Plugin_Activate", "Platform_Plugin_Deactivate", "Platform_Plugin_Retire"])
|
|
145
140
|
)
|
|
146
141
|
|
|
147
142
|
// The `@noApi` protocol variants come back from the same walk. They belong on
|
|
@@ -179,16 +174,20 @@ describe("the internal commands declare the edges they move rows along", () => {
|
|
|
179
174
|
}
|
|
180
175
|
|
|
181
176
|
testSync("the only edge into Disconnected is declared", () =>
|
|
182
|
-
expect((
|
|
183
|
-
|
|
177
|
+
expect((
|
|
178
|
+
commandNamed("Disconnect").allowedStates,
|
|
179
|
+
commandNamed("Disconnect").targetState,
|
|
180
|
+
))->toEqual((Some(["Connected"]), Some("Disconnected")))
|
|
184
181
|
)
|
|
185
182
|
|
|
186
183
|
// The creating form's claim: a target and no from-set. `Connect` brings the
|
|
187
184
|
// version's row into being, so there is no state it runs from — and an empty
|
|
188
185
|
// `allowedStates` would say the opposite, that it is legal in none.
|
|
189
186
|
testSync("the handshake declares a target and no from-set", () =>
|
|
190
|
-
expect((commandNamed("Connect").allowedStates, commandNamed("Connect").targetState))
|
|
191
|
-
|
|
187
|
+
expect((commandNamed("Connect").allowedStates, commandNamed("Connect").targetState))->toEqual((
|
|
188
|
+
None,
|
|
189
|
+
Some("Connected"),
|
|
190
|
+
))
|
|
192
191
|
)
|
|
193
192
|
|
|
194
193
|
testSync("and no state is left unreachable", () =>
|
|
@@ -91,8 +91,7 @@ let structure: pluginStructure = {
|
|
|
91
91
|
|
|
92
92
|
let structures = [("Ordering@1.0.0", structure)]
|
|
93
93
|
|
|
94
|
-
let bake = selections =>
|
|
95
|
-
Platform_BakedManifest.curate(~structures, ~selections)
|
|
94
|
+
let bake = selections => Platform_BakedManifest.curate(~structures, ~selections)
|
|
96
95
|
|
|
97
96
|
// Every case below varies one include-list and means "everything" by the rest,
|
|
98
97
|
// which is exactly what an omitted labelled argument says.
|
|
@@ -112,7 +111,10 @@ let names = (entry: JSON.t, field: string) =>
|
|
|
112
111
|
->Option.flatMap(JSON.Decode.array)
|
|
113
112
|
->Option.getOr([])
|
|
114
113
|
->Array.filterMap(v =>
|
|
115
|
-
v
|
|
114
|
+
v
|
|
115
|
+
->JSON.Decode.object
|
|
116
|
+
->Option.flatMap(d => d->Dict.get("name"))
|
|
117
|
+
->Option.flatMap(JSON.Decode.string)
|
|
116
118
|
)
|
|
117
119
|
|
|
118
120
|
let firstEntry = (r: result<JSON.t, Platform_BakedManifest.error>) =>
|
|
@@ -146,15 +148,13 @@ describe("curate", () => {
|
|
|
146
148
|
// from, so an Internal view a kept command points at has to survive the bake.
|
|
147
149
|
testSync("carries an Internal view referenced by an included command", () => {
|
|
148
150
|
let entry =
|
|
149
|
-
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])
|
|
150
|
-
->firstEntry
|
|
151
|
+
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])->firstEntry
|
|
151
152
|
expect(entry->names("internalQueryables"))->toEqual(["AvailableProducts"])
|
|
152
153
|
})
|
|
153
154
|
|
|
154
155
|
testSync("omits an Internal view no included command references", () => {
|
|
155
156
|
let entry =
|
|
156
|
-
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["ImportOrders"])])
|
|
157
|
-
->firstEntry
|
|
157
|
+
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["ImportOrders"])])->firstEntry
|
|
158
158
|
expect(entry->names("internalQueryables"))->toEqual([])
|
|
159
159
|
})
|
|
160
160
|
|
|
@@ -193,9 +193,9 @@ describe("curate", () => {
|
|
|
193
193
|
)
|
|
194
194
|
|
|
195
195
|
testSync("accepts a kind this plugin generates no page for", () =>
|
|
196
|
-
expect(
|
|
197
|
-
|
|
198
|
-
)
|
|
196
|
+
expect(bake([sel(~plugin="Ordering", ~derived=["scheduler"])])->firstEntry->derivedOf)->toEqual(
|
|
197
|
+
Some(["scheduler"]),
|
|
198
|
+
)
|
|
199
199
|
)
|
|
200
200
|
|
|
201
201
|
testSync("an Internal view is never selectable as a view", () =>
|
|
@@ -228,27 +228,26 @@ describe("curate", () => {
|
|
|
228
228
|
})
|
|
229
229
|
|
|
230
230
|
testSync("one entry per selection, in declaration order", () => {
|
|
231
|
-
let json =
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
~
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
->Array.filterMap(e =>
|
|
231
|
+
let json = Platform_BakedManifest.curate(
|
|
232
|
+
~structures=[("Ordering@1.0.0", structure), ("Catalog@1.0.0", structure)],
|
|
233
|
+
~selections=[
|
|
234
|
+
sel(~plugin="Catalog", ~views=[], ~commands=[]),
|
|
235
|
+
sel(~plugin="Ordering", ~views=[], ~commands=[]),
|
|
236
|
+
],
|
|
237
|
+
)
|
|
238
|
+
let ids = switch json {
|
|
239
|
+
| Ok(j) =>
|
|
240
|
+
j
|
|
241
|
+
->entries
|
|
242
|
+
->Array.filterMap(
|
|
243
|
+
e =>
|
|
245
244
|
e
|
|
246
245
|
->JSON.Decode.object
|
|
247
246
|
->Option.flatMap(d => d->Dict.get("pluginId"))
|
|
248
|
-
->Option.flatMap(JSON.Decode.string)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
->Option.flatMap(JSON.Decode.string),
|
|
248
|
+
)
|
|
249
|
+
| Error(_) => []
|
|
250
|
+
}
|
|
252
251
|
expect(ids)->toEqual(["Catalog", "Ordering"])
|
|
253
252
|
})
|
|
254
253
|
|
|
@@ -258,8 +257,7 @@ describe("curate", () => {
|
|
|
258
257
|
// makes this hold — the assertion guards that property, not a second code path.
|
|
259
258
|
testSync("a curated entry carries the owner field the served entry carries", () => {
|
|
260
259
|
let entry =
|
|
261
|
-
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])
|
|
262
|
-
->firstEntry
|
|
260
|
+
bake([sel(~plugin="Ordering", ~views=["Orders"], ~commands=["PlaceOrder"])])->firstEntry
|
|
263
261
|
let ownerOf = (key, name) =>
|
|
264
262
|
entry
|
|
265
263
|
->JSON.Decode.object
|
|
@@ -267,9 +265,7 @@ describe("curate", () => {
|
|
|
267
265
|
->Option.flatMap(JSON.Decode.array)
|
|
268
266
|
->Option.getOr([])
|
|
269
267
|
->Array.filterMap(JSON.Decode.object)
|
|
270
|
-
->Array.find(d =>
|
|
271
|
-
d->Dict.get("name")->Option.flatMap(JSON.Decode.string) == Some(name)
|
|
272
|
-
)
|
|
268
|
+
->Array.find(d => d->Dict.get("name")->Option.flatMap(JSON.Decode.string) == Some(name))
|
|
273
269
|
->Option.flatMap(d => d->Dict.get("ownerField"))
|
|
274
270
|
->Option.flatMap(JSON.Decode.string)
|
|
275
271
|
let commandOwner =
|
|
@@ -279,8 +275,8 @@ describe("curate", () => {
|
|
|
279
275
|
->Option.flatMap(JSON.Decode.array)
|
|
280
276
|
->Option.getOr([])
|
|
281
277
|
->Array.filterMap(JSON.Decode.object)
|
|
282
|
-
->Array.flatMap(
|
|
283
|
-
w->Dict.get("commands")->Option.flatMap(JSON.Decode.array)->Option.getOr([])
|
|
278
|
+
->Array.flatMap(
|
|
279
|
+
w => w->Dict.get("commands")->Option.flatMap(JSON.Decode.array)->Option.getOr([]),
|
|
284
280
|
)
|
|
285
281
|
->Array.filterMap(JSON.Decode.object)
|
|
286
282
|
->Array.get(0)
|
|
@@ -11,9 +11,7 @@ let cmd: commandDef = {
|
|
|
11
11
|
level: Collection,
|
|
12
12
|
aggregateIdField: None,
|
|
13
13
|
mutationField: "AddProduct",
|
|
14
|
-
references: [
|
|
15
|
-
{fieldName: "categoryId", entity: "Category", plugin: Some("Catalog")},
|
|
16
|
-
],
|
|
14
|
+
references: [{fieldName: "categoryId", entity: "Category", plugin: Some("Catalog")}],
|
|
17
15
|
allowedStates: None,
|
|
18
16
|
targetState: None,
|
|
19
17
|
apiExposed: Some(true),
|
|
@@ -276,10 +274,10 @@ describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
|
276
274
|
idField: Some("productId"),
|
|
277
275
|
idFieldSource: Some("sole"),
|
|
278
276
|
requiredAccess: None,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
277
|
+
ownerField: None,
|
|
278
|
+
retiredField: None,
|
|
279
|
+
retiredValues: None,
|
|
280
|
+
namedWhenRetired: None,
|
|
283
281
|
}
|
|
284
282
|
// A distinct name per source array: the complement has to be fed by both, and
|
|
285
283
|
// reusing one def would hide a version that only walks `readModels`.
|
|
@@ -293,8 +291,10 @@ describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
|
293
291
|
readModels: [qbl, internalQbl],
|
|
294
292
|
stateViewSlices: [internalSlice],
|
|
295
293
|
}
|
|
296
|
-
let mixedEntry =
|
|
297
|
-
|
|
294
|
+
let mixedEntry = Platform_ComponentDefinitionsApi.encodePluginStructureEntry(
|
|
295
|
+
~pluginId="Ordering",
|
|
296
|
+
mixed,
|
|
297
|
+
)
|
|
298
298
|
|
|
299
299
|
// Names under one key of the encoded entry. `String.includes` over the whole
|
|
300
300
|
// JSON can no longer answer this: an Internal view is legitimately present
|
|
@@ -308,7 +308,10 @@ describe("visibility filtering (deployed AutoUI hides Internal)", () => {
|
|
|
308
308
|
->Option.flatMap(JSON.Decode.array)
|
|
309
309
|
->Option.getOr([])
|
|
310
310
|
->Array.filterMap(c =>
|
|
311
|
-
c
|
|
311
|
+
c
|
|
312
|
+
->JSON.Decode.object
|
|
313
|
+
->Option.flatMap(o => o->Dict.get("name"))
|
|
314
|
+
->Option.flatMap(JSON.Decode.string)
|
|
312
315
|
)
|
|
313
316
|
|
|
314
317
|
testSync("excludes an Internal queryableDef from the encoded read-side", () => {
|
|
@@ -358,7 +361,7 @@ describe("allowedStates + lifecycleField populated", () => {
|
|
|
358
361
|
allowedStatesSource: "declared",
|
|
359
362
|
apiExposed: Some(true),
|
|
360
363
|
requiredAccess: None,
|
|
361
|
-
|
|
364
|
+
ownerField: None,
|
|
362
365
|
}
|
|
363
366
|
|
|
364
367
|
let qblWithStatus: queryableDef = {
|
|
@@ -377,10 +380,10 @@ describe("allowedStates + lifecycleField populated", () => {
|
|
|
377
380
|
idField: None,
|
|
378
381
|
idFieldSource: None,
|
|
379
382
|
requiredAccess: None,
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
383
|
+
ownerField: None,
|
|
384
|
+
retiredField: None,
|
|
385
|
+
retiredValues: None,
|
|
386
|
+
namedWhenRetired: None,
|
|
384
387
|
}
|
|
385
388
|
|
|
386
389
|
let wblWithStates: writableDef = {
|
|
@@ -84,7 +84,10 @@ let namesOf = (key): array<string> =>
|
|
|
84
84
|
->Option.flatMap(JSON.Decode.array)
|
|
85
85
|
->Option.getOr([])
|
|
86
86
|
->Array.filterMap(v =>
|
|
87
|
-
v
|
|
87
|
+
v
|
|
88
|
+
->JSON.Decode.object
|
|
89
|
+
->Option.flatMap(o => o->Dict.get("name"))
|
|
90
|
+
->Option.flatMap(JSON.Decode.string)
|
|
88
91
|
)
|
|
89
92
|
|
|
90
93
|
describe("Platform_PluginStructures entry", () => {
|
|
@@ -130,8 +133,7 @@ describe("Platform_PluginStructures entry", () => {
|
|
|
130
133
|
expect(
|
|
131
134
|
json->String.includes(
|
|
132
135
|
"\"extensionPoints\":[{\"name\":\"Catalog.Products\",\"delegateNames\":[\"onAdded\"],\"sourceEventTypes\":[\"Catalog.ProductAdded\"],\"commandTypes\":null," ++
|
|
133
|
-
|
|
134
|
-
"\"acceptedCommands\":[{\"name\":\"Catalog.Products.Reserve\",\"toCommandTypes\":[\"Catalog.ReserveStock\"]}]}]",
|
|
136
|
+
"\"publishedEvents\":[{\"name\":\"Catalog.Products.ProductBecameAvailable\",\"fromEventTypes\":[\"Catalog.ProductAdded\"]}]," ++ "\"acceptedCommands\":[{\"name\":\"Catalog.Products.Reserve\",\"toCommandTypes\":[\"Catalog.ReserveStock\"]}]}]",
|
|
135
137
|
),
|
|
136
138
|
)->toEqual(true)
|
|
137
139
|
)
|
|
@@ -57,7 +57,9 @@ describe("encodeUIFragmentEntry", () => {
|
|
|
57
57
|
|
|
58
58
|
testSync("encodes remoteEntryUrl", () =>
|
|
59
59
|
expect(
|
|
60
|
-
json->String.includes(
|
|
60
|
+
json->String.includes(
|
|
61
|
+
"\"remoteEntryUrl\":\"https://cdn.example.com/catalog@1.0/remoteEntry.js\"",
|
|
62
|
+
),
|
|
61
63
|
)->toEqual(true)
|
|
62
64
|
)
|
|
63
65
|
|
|
@@ -80,8 +82,9 @@ describe("encodeUIFragmentEntry", () => {
|
|
|
80
82
|
|
|
81
83
|
describe("Platform_UIFragmentsApi.sdl", () => {
|
|
82
84
|
testSync("query field declares the list type", () =>
|
|
83
|
-
expect(
|
|
84
|
-
|
|
85
|
+
expect(
|
|
86
|
+
Platform_UIFragmentsApi.sdlQueryField->String.includes("[Platform_UIFragmentEntry!]!"),
|
|
87
|
+
)->toEqual(true)
|
|
85
88
|
)
|
|
86
89
|
|
|
87
90
|
testSync("sdlTypes includes Platform_UIFragmentEntry", () => {
|