@reventlessdev/reventless-core 3.0.0-alpha.258 → 3.0.0-alpha.259
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 +11 -0
- package/package.json +6 -6
- package/src/Projection.res +158 -40
- package/src/Projection.res.mjs +107 -28
- package/src/admin/UiFragmentRegistry/{StateChangeSlice → StateChange}/UiFragmentRegistry.res.mjs +1 -1
- package/src/admin/UiFragmentRegistry/{StateChangeSlice → StateChange}/UiFragmentRegistry_Behavior.res.mjs +1 -1
- package/src/admin/UiFragmentRegistry/{StateViewSlice → StateView}/UiFragments.res.mjs +1 -1
- package/src/admin/UiFragmentRegistry/{StateViewSlice → StateView}/UiFragments_Projection.res.mjs +1 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res +12 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +2 -1
- package/src/components/Api/SchemaType.res +66 -1
- package/src/components/Api/SchemaType.res.mjs +66 -3
- package/src/components/Api/SuryToJsonSchema.res +1 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +11 -0
- package/src/components/ReadModel/ReadModel_Callback.res +5 -2
- package/src/components/ReadModel/ReadModel_Callback.res.mjs +2 -5
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +7 -6
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +2 -5
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +3 -1
- package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +1 -1
- package/src/plugin/component/Plugin_Structure.res +9 -46
- package/src/plugin/component/Plugin_Structure.res.mjs +8 -40
- package/src/plugin/connect/PluginExtensionPoint_UiFragment.res.mjs +1 -1
- package/tests/admin/AdminApiSchemaDriftTest.res.mjs +1 -1
- package/tests/api/BrandedMarkerCompositionTest.res +74 -0
- package/tests/api/BrandedMarkerCompositionTest.res.mjs +74 -0
- package/tests/api/PsBrandedMarkersReadModel.res +24 -0
- package/tests/api/PsBrandedMarkersReadModel.res.mjs +87 -0
- package/tests/api/SuryToJsonSchemaTest.res +35 -3
- package/tests/api/SuryToJsonSchemaTest.res.mjs +35 -4
- package/tests/plugin/PluginStructureAccessTest.res.mjs +3 -3
- package/tests/plugin/PluginStructureTest.res +5 -5
- package/tests/plugin/PluginStructureTest.res.mjs +26 -26
- package/tests/plugin/{StateChangeSlice → StateChange}/PsAttachInvoice.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsChangePhoto.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsDispatchShipment.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsGatedCommands.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsPlaceOrder.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsReserveStock.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsShipOrder.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsTransitionSwitch.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsTypoStore.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsUploadAvatar.res.mjs +1 -1
- package/tests/plugin/{StateChangeSlice → StateChange}/PsUploadImages.res.mjs +1 -1
- package/tests/plugin/{StateViewSlice → StateView}/PsAnnotatedView.res.mjs +1 -1
- package/tests/plugin/{StateViewSlice → StateView}/PsAvailableProductsView.res.mjs +1 -1
- package/tests/plugin/{StateViewSlice → StateView}/PsCategoriesView.res.mjs +1 -1
- package/tests/plugin/{StateViewSlice → StateView}/PsCustomersView.res.mjs +1 -1
- package/tests/plugin/{StateViewSlice → StateView}/PsGatedView.res.mjs +1 -1
- package/tests/plugin/{StateViewSlice → StateView}/PsOrdersView.res.mjs +1 -1
- package/tests/plugin/{StateViewSlice → StateView}/PsShipmentsView.res.mjs +1 -1
- package/tests/projection/LifecycleTrailTest.res +139 -0
- package/tests/projection/LifecycleTrailTest.res.mjs +280 -0
- /package/src/admin/UiFragmentRegistry/{StateChangeSlice → StateChange}/UiFragmentRegistry.res +0 -0
- /package/src/admin/UiFragmentRegistry/{StateChangeSlice → StateChange}/UiFragmentRegistry_Behavior.res +0 -0
- /package/src/admin/UiFragmentRegistry/{StateViewSlice → StateView}/UiFragments.res +0 -0
- /package/src/admin/UiFragmentRegistry/{StateViewSlice → StateView}/UiFragments_Projection.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsAttachInvoice.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsChangePhoto.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsDispatchShipment.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsGatedCommands.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsPlaceOrder.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsReserveStock.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsShipOrder.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsTransitionSwitch.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsTypoStore.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsUploadAvatar.res +0 -0
- /package/tests/plugin/{StateChangeSlice → StateChange}/PsUploadImages.res +0 -0
- /package/tests/plugin/{StateViewSlice → StateView}/PsAnnotatedView.res +0 -0
- /package/tests/plugin/{StateViewSlice → StateView}/PsAvailableProductsView.res +0 -0
- /package/tests/plugin/{StateViewSlice → StateView}/PsCategoriesView.res +0 -0
- /package/tests/plugin/{StateViewSlice → StateView}/PsCustomersView.res +0 -0
- /package/tests/plugin/{StateViewSlice → StateView}/PsGatedView.res +0 -0
- /package/tests/plugin/{StateViewSlice → StateView}/PsOrdersView.res +0 -0
- /package/tests/plugin/{StateViewSlice → StateView}/PsShipmentsView.res +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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.259 (2026-09-08)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* a slice folder is named for its kind, without the Slice ([a400555](https://github.com/ReventlessDev/reventless-core/commit/a400555121b023823983ebe846e99cda27063c1f))
|
|
11
|
+
* **core:** a reference names a row and shows its picture ([2b40d04](https://github.com/ReventlessDev/reventless-core/commit/2b40d0418bba2bf83463f446e94e372ea0c9898c))
|
|
12
|
+
* **core:** a state view records when it reached each state ([5216f71](https://github.com/ReventlessDev/reventless-core/commit/5216f71b8dcafcb2f0c991923ebcf2438b27e347))
|
|
13
|
+
* **ppx:** a marker composes onto a branded type instead of replacing it ([250c455](https://github.com/ReventlessDev/reventless-core/commit/250c4559aec142558adadcb7f539fea607da5032))
|
|
14
|
+
* **spec:** an instant is a type, and a calendar day is a different one ([a60bebc](https://github.com/ReventlessDev/reventless-core/commit/a60bebc20fe5f3413f9252be36f94ba2a9fb620d))
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# 3.0.0-alpha.258 (2026-09-07)
|
|
7
18
|
|
|
8
19
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.259",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
31
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
32
32
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
|
|
33
|
-
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.9",
|
|
34
33
|
"@reventlessdev/rescript-node": "2.0.0-alpha.9",
|
|
34
|
+
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.9",
|
|
35
35
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
36
|
-
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.9",
|
|
37
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.159",
|
|
38
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
37
|
+
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.9",
|
|
39
38
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
40
|
-
"@reventlessdev/reventless-
|
|
41
|
-
"@reventlessdev/reventless-interop": "3.0.0-alpha.35"
|
|
39
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.160",
|
|
40
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.35",
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.132"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "12.3.0",
|
package/src/Projection.res
CHANGED
|
@@ -21,54 +21,172 @@ module Mapping = {
|
|
|
21
21
|
// hot path at the default Info level.
|
|
22
22
|
let logAction = makeStr => log.debugLazy(~comp="Projection", makeStr)
|
|
23
23
|
|
|
24
|
-
// ──
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// the
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
// ── Schema-driven state overlays ────────────────────────────────────────────
|
|
25
|
+
// Two things every state a projection writes carries that the domain does not
|
|
26
|
+
// write: the composite `@displayName` label, and a lifecycle trail entry for an
|
|
27
|
+
// action that moved the row's lifecycle field. Both are read off the state
|
|
28
|
+
// schema and applied to one JSON round trip per written state.
|
|
29
|
+
|
|
30
|
+
type overlays = {
|
|
31
|
+
displayName: option<Reventless.DisplayName.displayNameSpec>,
|
|
32
|
+
// (trail field, lifecycle field). A trail with no lifecycle field to follow
|
|
33
|
+
// records nothing — there is no value to append.
|
|
34
|
+
trail: option<(string, string)>,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let overlaysFor = (stateSchema: S.t<unknown>): option<overlays> => {
|
|
38
|
+
let displayName = Reventless.DisplayName.getSpec(stateSchema)
|
|
39
|
+
let trail = switch (
|
|
40
|
+
Reventless.Lifecycle.Trail.fieldName(stateSchema),
|
|
41
|
+
Reventless.Lifecycle.fieldName(stateSchema),
|
|
42
|
+
) {
|
|
43
|
+
| (Some(trailField), Some(lifecycleField)) => Some((trailField, lifecycleField))
|
|
44
|
+
| _ => None
|
|
45
|
+
}
|
|
46
|
+
displayName->Option.isNone && trail->Option.isNone ? None : Some({displayName, trail})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// `priorLifecycle` is what the row's lifecycle field held before the action.
|
|
50
|
+
// `None` where the row is created — that is a change from nothing, and it opens
|
|
51
|
+
// the trail. `at` is the envelope's own time, never a clock: a projection that
|
|
52
|
+
// read one would produce a different trail on every replay.
|
|
53
|
+
let applyOverlays = (
|
|
54
|
+
stateDict: dict<JSON.t>,
|
|
55
|
+
{displayName, trail}: overlays,
|
|
56
|
+
~priorLifecycle: option<JSON.t>,
|
|
57
|
+
~at: string,
|
|
58
|
+
): unit => {
|
|
59
|
+
displayName->Option.forEach(spec =>
|
|
60
|
+
stateDict->Dict.set(
|
|
61
|
+
"displayName",
|
|
62
|
+
JSON.Encode.string(Reventless.DisplayName.computeLabel(spec, stateDict)),
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
trail->Option.forEach(((trailField, lifecycleField)) =>
|
|
66
|
+
switch stateDict->Dict.get(lifecycleField) {
|
|
67
|
+
| Some(value) if Some(value) != priorLifecycle =>
|
|
68
|
+
stateDict->Reventless.Lifecycle.Trail.record(~field=trailField, ~state=value, ~at)
|
|
69
|
+
| _ => ()
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// The action shapes, once, over a lens onto whatever a state is on this path:
|
|
75
|
+
// a typed record encoded through sury, or JSON that already is one.
|
|
76
|
+
//
|
|
77
|
+
// Multi-state actions get the label but no trail entry: pairing a before-row
|
|
78
|
+
// with an after-row needs the sub-id, which is not resolved until the action is
|
|
79
|
+
// applied, and pairing by position would credit one row's transition to another.
|
|
80
|
+
let rewriteActionWith = (
|
|
81
|
+
action: action<'id, 'state>,
|
|
82
|
+
~overlays: overlays,
|
|
83
|
+
~toDict: 'state => option<dict<JSON.t>>,
|
|
84
|
+
~fromDict: dict<JSON.t> => 'state,
|
|
85
|
+
~at: string,
|
|
86
|
+
): action<'id, 'state> => {
|
|
87
|
+
// Read before the update function runs, so a projection that returns its
|
|
88
|
+
// argument mutated cannot make the prior state look like the new one.
|
|
89
|
+
let priorLifecycleOf = state =>
|
|
90
|
+
switch overlays.trail {
|
|
91
|
+
| None => None
|
|
92
|
+
| Some((_, lifecycleField)) => toDict(state)->Option.flatMap(Dict.get(_, lifecycleField))
|
|
93
|
+
}
|
|
94
|
+
let apply = (state, ~overlays, ~priorLifecycle) =>
|
|
95
|
+
switch toDict(state) {
|
|
96
|
+
| None => state
|
|
97
|
+
| Some(stateDict) =>
|
|
98
|
+
applyOverlays(stateDict, overlays, ~priorLifecycle, ~at)
|
|
99
|
+
fromDict(stateDict)
|
|
100
|
+
}
|
|
101
|
+
// `Set` opens a trail rather than extending one: it replaces the whole row,
|
|
102
|
+
// the trail included, so there is no prior value left to compare against.
|
|
103
|
+
let created = state => apply(state, ~overlays, ~priorLifecycle=None)
|
|
104
|
+
let updated = (fn, state) =>
|
|
105
|
+
apply(fn(state), ~overlays, ~priorLifecycle=priorLifecycleOf(state))
|
|
106
|
+
let labelled = state => apply(state, ~overlays={...overlays, trail: None}, ~priorLifecycle=None)
|
|
107
|
+
switch action {
|
|
108
|
+
| Create(id, state) => Create(id, created(state))
|
|
109
|
+
| CreateMany(pairs) => CreateMany(pairs->Array.map(((id, s)) => (id, created(s))))
|
|
110
|
+
| Set(id, state) => Set(id, created(state))
|
|
111
|
+
| SetMany(ids, fn) => SetMany(ids, id => created(fn(id)))
|
|
112
|
+
| Update(id, fn) => Update(id, state => updated(fn, state))
|
|
113
|
+
| UpdateMany(ids, fn) => UpdateMany(ids, (id, s) => updated(state => fn(id, state), s))
|
|
114
|
+
| UpdateWithDefault(id, default, fn) =>
|
|
115
|
+
UpdateWithDefault(id, created(default), state => updated(fn, state))
|
|
116
|
+
| UpdateManyWithDefault(ids, defFn, fn) =>
|
|
117
|
+
UpdateManyWithDefault(
|
|
118
|
+
ids,
|
|
119
|
+
id => created(defFn(id)),
|
|
120
|
+
(id, s) => updated(state => fn(id, state), s),
|
|
121
|
+
)
|
|
122
|
+
| CreateMultiState(id, states) => CreateMultiState(id, states->Array.map(labelled))
|
|
123
|
+
| UpdateMultiState(id, fn) => UpdateMultiState(id, states => states->fn->Array.map(labelled))
|
|
124
|
+
| UpdateManyMultiStates(ids, fn) =>
|
|
125
|
+
UpdateManyMultiStates(ids, (id, states) => fn(id, states)->Array.map(labelled))
|
|
126
|
+
| Delete(_) | DeleteMany(_) | DeleteIf(_, _) | DeleteManyIf(_, _) | Ignore => action
|
|
43
127
|
}
|
|
44
128
|
}
|
|
45
129
|
|
|
46
130
|
let rewriteAction = (
|
|
47
131
|
action: action<'id, 'state>,
|
|
132
|
+
~at: string,
|
|
48
133
|
stateSchema: S.t<'state>,
|
|
49
134
|
): action<'id, 'state> =>
|
|
50
|
-
switch
|
|
135
|
+
switch overlaysFor(stateSchema->S.castToUnknown) {
|
|
51
136
|
| None => action
|
|
52
|
-
| Some(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
137
|
+
| Some(overlays) =>
|
|
138
|
+
action->rewriteActionWith(
|
|
139
|
+
~overlays,
|
|
140
|
+
~at,
|
|
141
|
+
~toDict=state => state->Reventless.Util_Sury.toJson(stateSchema)->JSON.Decode.object,
|
|
142
|
+
~fromDict=stateDict =>
|
|
143
|
+
stateDict->JSON.Encode.object->Reventless.Util_Sury.fromJson(stateSchema),
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The trail alone, for the two callers that must not compose `displayName`.
|
|
148
|
+
let trailOverlays = (stateSchema: option<S.t<unknown>>): option<overlays> =>
|
|
149
|
+
switch stateSchema->Option.flatMap(overlaysFor) {
|
|
150
|
+
| Some({trail: Some(_)} as overlays) => Some({...overlays, displayName: None})
|
|
151
|
+
| _ => None
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
The rewrite a test harness applies: the trail and nothing else. `displayName`
|
|
156
|
+
lives in the state schema and not in the record, so composing it would produce a
|
|
157
|
+
state no expectation can spell.
|
|
158
|
+
*/
|
|
159
|
+
let rewriteTrail = (
|
|
160
|
+
action: action<'id, 'state>,
|
|
161
|
+
~at: string,
|
|
162
|
+
stateSchema: S.t<'state>,
|
|
163
|
+
): action<'id, 'state> =>
|
|
164
|
+
switch trailOverlays(Some(stateSchema->S.castToUnknown)) {
|
|
165
|
+
| None => action
|
|
166
|
+
| Some(overlays) =>
|
|
167
|
+
action->rewriteActionWith(
|
|
168
|
+
~overlays,
|
|
169
|
+
~at,
|
|
170
|
+
~toDict=state => state->Reventless.Util_Sury.toJson(stateSchema)->JSON.Decode.object,
|
|
171
|
+
~fromDict=stateDict =>
|
|
172
|
+
stateDict->JSON.Encode.object->Reventless.Util_Sury.fromJson(stateSchema),
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
The same, for the JSON-level pipeline a deployed state-view slice assembles,
|
|
178
|
+
whose states are already JSON. `@displayName` is composed at save there
|
|
179
|
+
(`ProjectionEntryPoint_Ops.withDisplayName`).
|
|
180
|
+
*/
|
|
181
|
+
let rewriteJsonAction = (
|
|
182
|
+
action: action<'id, JSON.t>,
|
|
183
|
+
~at: string,
|
|
184
|
+
~stateSchema: option<S.t<unknown>>,
|
|
185
|
+
): action<'id, JSON.t> =>
|
|
186
|
+
switch trailOverlays(stateSchema) {
|
|
187
|
+
| None => action
|
|
188
|
+
| Some(overlays) =>
|
|
189
|
+
action->rewriteActionWith(~overlays, ~at, ~toDict=JSON.Decode.object, ~fromDict=JSON.Encode.object)
|
|
72
190
|
}
|
|
73
191
|
|
|
74
192
|
let applyChanges = async (
|
package/src/Projection.res.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
|
|
|
11
11
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
12
12
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
13
13
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
14
|
+
import * as Lifecycle$Reventless from "@reventlessdev/reventless-spec/src/types/Lifecycle.res.mjs";
|
|
14
15
|
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
15
16
|
import * as Logger$ReventlessCore from "./util/Logger.res.mjs";
|
|
16
17
|
import * as DisplayName$Reventless from "@reventlessdev/reventless-spec/src/components/DisplayName.res.mjs";
|
|
@@ -37,23 +38,59 @@ function logAction(makeStr) {
|
|
|
37
38
|
log.debugLazy("Projection", makeStr);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
function
|
|
41
|
-
let
|
|
42
|
-
let
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
function overlaysFor(stateSchema) {
|
|
42
|
+
let displayName = DisplayName$Reventless.getSpec(stateSchema);
|
|
43
|
+
let match = Lifecycle$Reventless.Trail.fieldName(stateSchema);
|
|
44
|
+
let match$1 = Lifecycle$Reventless.fieldName(stateSchema);
|
|
45
|
+
let trail = match !== undefined && match$1 !== undefined ? [
|
|
46
|
+
match,
|
|
47
|
+
match$1
|
|
48
|
+
] : undefined;
|
|
49
|
+
if (Stdlib_Option.isNone(displayName) && Stdlib_Option.isNone(trail)) {
|
|
50
|
+
return;
|
|
51
|
+
} else {
|
|
52
|
+
return {
|
|
53
|
+
displayName: displayName,
|
|
54
|
+
trail: trail
|
|
55
|
+
};
|
|
45
56
|
}
|
|
46
|
-
let label = DisplayName$Reventless.computeLabel(spec, stateDict);
|
|
47
|
-
stateDict["displayName"] = label;
|
|
48
|
-
return Util_Sury$Reventless.fromJson(stateDict, stateSchema);
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
function applyOverlays(stateDict, param, priorLifecycle, at) {
|
|
60
|
+
Stdlib_Option.forEach(param.displayName, spec => {
|
|
61
|
+
stateDict["displayName"] = DisplayName$Reventless.computeLabel(spec, stateDict);
|
|
62
|
+
});
|
|
63
|
+
Stdlib_Option.forEach(param.trail, param => {
|
|
64
|
+
let value = stateDict[param[1]];
|
|
65
|
+
if (value !== undefined && Primitive_object.notequal(value, priorLifecycle)) {
|
|
66
|
+
return Lifecycle$Reventless.Trail.record(stateDict, param[0], value, at);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function rewriteActionWith(action, overlays, toDict, fromDict, at) {
|
|
72
|
+
let priorLifecycleOf = state => {
|
|
73
|
+
let match = overlays.trail;
|
|
74
|
+
if (match === undefined) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let lifecycleField = match[1];
|
|
78
|
+
return Stdlib_Option.flatMap(toDict(state), __x => __x[lifecycleField]);
|
|
79
|
+
};
|
|
80
|
+
let apply = (state, overlays, priorLifecycle) => {
|
|
81
|
+
let stateDict = toDict(state);
|
|
82
|
+
if (stateDict !== undefined) {
|
|
83
|
+
applyOverlays(stateDict, overlays, priorLifecycle, at);
|
|
84
|
+
return fromDict(stateDict);
|
|
85
|
+
} else {
|
|
86
|
+
return state;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
let updated = (fn, state) => apply(fn(state), overlays, priorLifecycleOf(state));
|
|
90
|
+
let labelled = state => apply(state, {
|
|
91
|
+
displayName: overlays.displayName,
|
|
92
|
+
trail: undefined
|
|
93
|
+
}, undefined);
|
|
57
94
|
if (typeof action !== "object") {
|
|
58
95
|
return action;
|
|
59
96
|
}
|
|
@@ -62,14 +99,14 @@ function rewriteAction(action, stateSchema) {
|
|
|
62
99
|
return {
|
|
63
100
|
TAG: "Create",
|
|
64
101
|
_0: action._0,
|
|
65
|
-
_1:
|
|
102
|
+
_1: apply(action._1, overlays, undefined)
|
|
66
103
|
};
|
|
67
104
|
case "CreateMany" :
|
|
68
105
|
return {
|
|
69
106
|
TAG: "CreateMany",
|
|
70
107
|
_0: action._0.map(param => [
|
|
71
108
|
param[0],
|
|
72
|
-
|
|
109
|
+
apply(param[1], overlays, undefined)
|
|
73
110
|
])
|
|
74
111
|
};
|
|
75
112
|
case "Update" :
|
|
@@ -77,22 +114,22 @@ function rewriteAction(action, stateSchema) {
|
|
|
77
114
|
return {
|
|
78
115
|
TAG: "Update",
|
|
79
116
|
_0: action._0,
|
|
80
|
-
_1: state =>
|
|
117
|
+
_1: state => updated(fn, state)
|
|
81
118
|
};
|
|
82
119
|
case "UpdateMany" :
|
|
83
120
|
let fn$1 = action._1;
|
|
84
121
|
return {
|
|
85
122
|
TAG: "UpdateMany",
|
|
86
123
|
_0: action._0,
|
|
87
|
-
_1: (id, s) =>
|
|
124
|
+
_1: (id, s) => updated(state => fn$1(id, state), s)
|
|
88
125
|
};
|
|
89
126
|
case "UpdateWithDefault" :
|
|
90
127
|
let fn$2 = action._2;
|
|
91
128
|
return {
|
|
92
129
|
TAG: "UpdateWithDefault",
|
|
93
130
|
_0: action._0,
|
|
94
|
-
_1:
|
|
95
|
-
_2: state =>
|
|
131
|
+
_1: apply(action._1, overlays, undefined),
|
|
132
|
+
_2: state => updated(fn$2, state)
|
|
96
133
|
};
|
|
97
134
|
case "UpdateManyWithDefault" :
|
|
98
135
|
let fn$3 = action._2;
|
|
@@ -100,47 +137,84 @@ function rewriteAction(action, stateSchema) {
|
|
|
100
137
|
return {
|
|
101
138
|
TAG: "UpdateManyWithDefault",
|
|
102
139
|
_0: action._0,
|
|
103
|
-
_1: id =>
|
|
104
|
-
_2: (id, s) =>
|
|
140
|
+
_1: id => apply(defFn(id), overlays, undefined),
|
|
141
|
+
_2: (id, s) => updated(state => fn$3(id, state), s)
|
|
105
142
|
};
|
|
106
143
|
case "Set" :
|
|
107
144
|
return {
|
|
108
145
|
TAG: "Set",
|
|
109
146
|
_0: action._0,
|
|
110
|
-
_1:
|
|
147
|
+
_1: apply(action._1, overlays, undefined)
|
|
111
148
|
};
|
|
112
149
|
case "SetMany" :
|
|
113
150
|
let fn$4 = action._1;
|
|
114
151
|
return {
|
|
115
152
|
TAG: "SetMany",
|
|
116
153
|
_0: action._0,
|
|
117
|
-
_1: id =>
|
|
154
|
+
_1: id => apply(fn$4(id), overlays, undefined)
|
|
118
155
|
};
|
|
119
156
|
case "CreateMultiState" :
|
|
120
157
|
return {
|
|
121
158
|
TAG: "CreateMultiState",
|
|
122
159
|
_0: action._0,
|
|
123
|
-
_1: action._1.map(
|
|
160
|
+
_1: action._1.map(labelled)
|
|
124
161
|
};
|
|
125
162
|
case "UpdateMultiState" :
|
|
126
163
|
let fn$5 = action._1;
|
|
127
164
|
return {
|
|
128
165
|
TAG: "UpdateMultiState",
|
|
129
166
|
_0: action._0,
|
|
130
|
-
_1: states => fn$5(states).map(
|
|
167
|
+
_1: states => fn$5(states).map(labelled)
|
|
131
168
|
};
|
|
132
169
|
case "UpdateManyMultiStates" :
|
|
133
170
|
let fn$6 = action._1;
|
|
134
171
|
return {
|
|
135
172
|
TAG: "UpdateManyMultiStates",
|
|
136
173
|
_0: action._0,
|
|
137
|
-
_1: (id, states) => fn$6(id, states).map(
|
|
174
|
+
_1: (id, states) => fn$6(id, states).map(labelled)
|
|
138
175
|
};
|
|
139
176
|
default:
|
|
140
177
|
return action;
|
|
141
178
|
}
|
|
142
179
|
}
|
|
143
180
|
|
|
181
|
+
function rewriteAction(action, at, stateSchema) {
|
|
182
|
+
let overlays = overlaysFor(stateSchema);
|
|
183
|
+
if (overlays !== undefined) {
|
|
184
|
+
return rewriteActionWith(action, overlays, state => Stdlib_JSON.Decode.object(Util_Sury$Reventless.toJson(state, stateSchema)), stateDict => Util_Sury$Reventless.fromJson(stateDict, stateSchema), at);
|
|
185
|
+
} else {
|
|
186
|
+
return action;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function trailOverlays(stateSchema) {
|
|
191
|
+
let overlays = Stdlib_Option.flatMap(stateSchema, overlaysFor);
|
|
192
|
+
if (overlays !== undefined && overlays.trail !== undefined) {
|
|
193
|
+
return {
|
|
194
|
+
displayName: undefined,
|
|
195
|
+
trail: overlays.trail
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function rewriteTrail(action, at, stateSchema) {
|
|
201
|
+
let overlays = trailOverlays(stateSchema);
|
|
202
|
+
if (overlays !== undefined) {
|
|
203
|
+
return rewriteActionWith(action, overlays, state => Stdlib_JSON.Decode.object(Util_Sury$Reventless.toJson(state, stateSchema)), stateDict => Util_Sury$Reventless.fromJson(stateDict, stateSchema), at);
|
|
204
|
+
} else {
|
|
205
|
+
return action;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function rewriteJsonAction(action, at, stateSchema) {
|
|
210
|
+
let overlays = trailOverlays(stateSchema);
|
|
211
|
+
if (overlays !== undefined) {
|
|
212
|
+
return rewriteActionWith(action, overlays, Stdlib_JSON.Decode.object, prim => prim, at);
|
|
213
|
+
} else {
|
|
214
|
+
return action;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
144
218
|
async function applyChanges(action, id, beforeStates, afterStates, param, param$1) {
|
|
145
219
|
let getSubId = param$1.getSubId;
|
|
146
220
|
let subIdField = param$1.subIdField;
|
|
@@ -714,8 +788,13 @@ export {
|
|
|
714
788
|
log,
|
|
715
789
|
Mapping,
|
|
716
790
|
logAction,
|
|
717
|
-
|
|
791
|
+
overlaysFor,
|
|
792
|
+
applyOverlays,
|
|
793
|
+
rewriteActionWith,
|
|
718
794
|
rewriteAction,
|
|
795
|
+
trailOverlays,
|
|
796
|
+
rewriteTrail,
|
|
797
|
+
rewriteJsonAction,
|
|
719
798
|
applyChanges,
|
|
720
799
|
stateToString,
|
|
721
800
|
statesToString,
|
package/src/admin/UiFragmentRegistry/{StateChangeSlice → StateChange}/UiFragmentRegistry.res.mjs
RENAMED
|
@@ -58,7 +58,7 @@ let Id;
|
|
|
58
58
|
|
|
59
59
|
let consumedEventSchema = eventSchema;
|
|
60
60
|
|
|
61
|
-
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
61
|
+
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry.res.mjs";
|
|
62
62
|
|
|
63
63
|
let readConsistency = "EscalateOnRetry";
|
|
64
64
|
|
|
@@ -68,7 +68,7 @@ let Spec;
|
|
|
68
68
|
|
|
69
69
|
let initialState;
|
|
70
70
|
|
|
71
|
-
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
71
|
+
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry_Behavior.res.mjs";
|
|
72
72
|
|
|
73
73
|
export {
|
|
74
74
|
Spec,
|
|
@@ -42,7 +42,7 @@ let Id;
|
|
|
42
42
|
|
|
43
43
|
let subIdConfig;
|
|
44
44
|
|
|
45
|
-
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
45
|
+
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs";
|
|
46
46
|
|
|
47
47
|
let authorization = "AllowAuthenticated";
|
|
48
48
|
|
package/src/admin/UiFragmentRegistry/{StateViewSlice → StateView}/UiFragments_Projection.res.mjs
RENAMED
|
@@ -45,7 +45,7 @@ function project(param) {
|
|
|
45
45
|
|
|
46
46
|
let Spec;
|
|
47
47
|
|
|
48
|
-
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/
|
|
48
|
+
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments_Projection.res.mjs";
|
|
49
49
|
|
|
50
50
|
export {
|
|
51
51
|
Spec,
|
|
@@ -47,6 +47,7 @@ let rec fromSchemaType = (
|
|
|
47
47
|
| ScalarBigInt => `String${bang}`
|
|
48
48
|
| EntityId => `ID${bang}`
|
|
49
49
|
| DateTime => `String${bang}`
|
|
50
|
+
| CalendarDate => `String${bang}`
|
|
50
51
|
| Nullable(inner) => fromSchemaType(~required=false, ~asInput, inner, collectedTypes, seenTypes)
|
|
51
52
|
| ArrayOf(item) =>
|
|
52
53
|
let itemType = fromSchemaType(~required=true, ~asInput, item, collectedTypes, seenTypes)
|
|
@@ -594,8 +595,18 @@ let deriveByIdsQueryField = (
|
|
|
594
595
|
// is not whether the door exists but whether a *retired* row comes through it —
|
|
595
596
|
// without it this is a cheap label read that narrows exactly as every other door
|
|
596
597
|
// does.
|
|
598
|
+
// `image` travels beside `label` for the reason the label does: a card or a tile
|
|
599
|
+
// drawing a reference wants the row's picture as well as its name, and the
|
|
600
|
+
// alternative is a second query per grid asking the referenced view for one
|
|
601
|
+
// field. It stays inside the narrowness above — a ref path is what a reference
|
|
602
|
+
// already publishes, and a caller still cannot ask for anything else — and it is
|
|
603
|
+
// `null` for the views (most of them) that declare no picture.
|
|
604
|
+
//
|
|
605
|
+
// Emitted unconditionally for the same reason the door itself is: a field that
|
|
606
|
+
// appears once a view adopts an image would make adopting one a breaking schema
|
|
607
|
+
// change for every client that had learned this type's shape.
|
|
597
608
|
let deriveRefTypeSdl = (~returnTypeName: string): string =>
|
|
598
|
-
`type ${returnTypeName}Ref {\n id: ID!\n label: String!\n retired: Boolean!\n retiredState: String\n}`
|
|
609
|
+
`type ${returnTypeName}Ref {\n id: ID!\n label: String!\n image: String\n retired: Boolean!\n retiredState: String\n}`
|
|
599
610
|
|
|
600
611
|
// `retiredState` is null in two cases that do not need telling apart by a
|
|
601
612
|
// consumer: a live row, and a boolean-form retirement, where the field is the
|
|
@@ -52,6 +52,7 @@ function fromSchemaType(_required, _asInputOpt, _st, collectedTypes, seenTypes)
|
|
|
52
52
|
case "ScalarString" :
|
|
53
53
|
case "ScalarBigInt" :
|
|
54
54
|
case "DateTime" :
|
|
55
|
+
case "CalendarDate" :
|
|
55
56
|
case "Unknown" :
|
|
56
57
|
return `String` + bang;
|
|
57
58
|
}
|
|
@@ -455,7 +456,7 @@ function deriveByIdsQueryField(listFieldName, returnTypeName) {
|
|
|
455
456
|
}
|
|
456
457
|
|
|
457
458
|
function deriveRefTypeSdl(returnTypeName) {
|
|
458
|
-
return `type ` + returnTypeName + `Ref {\n id: ID!\n label: String!\n retired: Boolean!\n retiredState: String\n}`;
|
|
459
|
+
return `type ` + returnTypeName + `Ref {\n id: ID!\n label: String!\n image: String\n retired: Boolean!\n retiredState: String\n}`;
|
|
459
460
|
}
|
|
460
461
|
|
|
461
462
|
function deriveRefsQueryField(listFieldName, returnTypeName) {
|