@reventlessdev/reventless-core 3.0.0-alpha.235 → 3.0.0-alpha.236
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 +10 -0
- package/package.json +4 -4
- package/src/admin/Platform_BakedManifest.res +52 -0
- package/src/admin/Platform_BakedManifest.res.mjs +38 -0
- package/src/components/Dcb/Dcb_Builder.res +52 -3
- package/src/components/Dcb/Dcb_Builder.res.mjs +57 -1
- package/tests/dcb/DcbEventLogSchemaTest.res +77 -0
- package/tests/dcb/DcbEventLogSchemaTest.res.mjs +90 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
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.236 (2026-08-15)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **core:** describe a DCB event log by every event it carries ([047bc0b](https://github.com/ReventlessDev/reventless-core/commit/047bc0beb9be1468410fde37a7f787a3d68f92d2))
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **aws:** serve one baked manifest per journey ([16d6307](https://github.com/ReventlessDev/reventless-core/commit/16d63071f059cf1fc7025f9be9206dda51f71053))
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
# 3.0.0-alpha.235 (2026-08-15)
|
|
7
17
|
|
|
8
18
|
### Features
|
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.236",
|
|
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-fast-csv": "2.0.0-alpha.7",
|
|
33
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
34
33
|
"@reventlessdev/rescript-node": "2.0.0-alpha.7",
|
|
35
34
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
|
|
36
35
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
36
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.7",
|
|
38
38
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
39
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.142",
|
|
39
40
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.31",
|
|
40
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.114"
|
|
41
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.142"
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.114"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "12.3.0",
|
|
@@ -204,6 +204,58 @@ let journeyKey = (~group: string): string => {
|
|
|
204
204
|
`component-manifest-${slug}.json`
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
let toSelection = (s: ReventlessInfra.Platform.bakedManifestSelection): selection => {
|
|
208
|
+
plugin: s.plugin,
|
|
209
|
+
views: s.views,
|
|
210
|
+
commands: s.commands,
|
|
211
|
+
derived: s.derived,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** One declared journey, with everything a platform needs to serve it: the group
|
|
215
|
+
it serves, the file it is written to, and the include-list that decides the
|
|
216
|
+
file's contents. */
|
|
217
|
+
type journeyFile = {group: string, key: string, selections: array<selection>}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
The declared journeys, in declaration order, with their keys resolved.
|
|
221
|
+
|
|
222
|
+
The default journey is deliberately not among them: it is the file
|
|
223
|
+
`manifestUrl` points at, which is what a caller matching no declared group reads
|
|
224
|
+
and what every deployment had before journeys existed.
|
|
225
|
+
*/
|
|
226
|
+
let journeyFiles = (~config: ReventlessInfra.Platform.bakedManifest): array<journeyFile> =>
|
|
227
|
+
config.journeys
|
|
228
|
+
->Option.getOr([])
|
|
229
|
+
->Array.map(j => {
|
|
230
|
+
group: j.group,
|
|
231
|
+
key: j.key->Option.getOr(journeyKey(~group=j.group)),
|
|
232
|
+
selections: j.components->Array.map(toSelection),
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
Every file a bake declaration produces, as (key, selections) pairs.
|
|
237
|
+
|
|
238
|
+
The default journey first and always — it is what a caller matching no declared
|
|
239
|
+
group gets, which includes the no-bearer identity a local dev session starts
|
|
240
|
+
from — then one per declared journey.
|
|
241
|
+
|
|
242
|
+
Here rather than on either platform because several separate things have to
|
|
243
|
+
agree about this set on a deploy: the objects the bake writes, the IAM grant
|
|
244
|
+
that lets it write them, and the URLs `config.json` sends a caller to. A key
|
|
245
|
+
derived twice is a shop whose file is written where nothing fetches it, and the
|
|
246
|
+
symptom is an empty menu rather than a missing file.
|
|
247
|
+
*/
|
|
248
|
+
let files = (
|
|
249
|
+
~config: ReventlessInfra.Platform.bakedManifest,
|
|
250
|
+
): array<(string, array<selection>)> =>
|
|
251
|
+
[
|
|
252
|
+
(config.key->Option.getOr(defaultKey), config.components->Array.map(toSelection)),
|
|
253
|
+
]->Array.concat(journeyFiles(~config)->Array.map(j => (j.key, j.selections)))
|
|
254
|
+
|
|
255
|
+
/** Each declared journey's group and the URL a shell fetches its file from. */
|
|
256
|
+
let journeyUrls = (~config: ReventlessInfra.Platform.bakedManifest): array<(string, string)> =>
|
|
257
|
+
journeyFiles(~config)->Array.map(j => (j.group, urlForKey(Some(j.key))))
|
|
258
|
+
|
|
207
259
|
// The whole file: one entry per selection, in the order the deployment declared
|
|
208
260
|
// them, so the include-list also states the order a consumer reads plugins in.
|
|
209
261
|
let curate = (
|
|
@@ -192,6 +192,40 @@ function journeyKey(group) {
|
|
|
192
192
|
return `component-manifest-` + slug + `.json`;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
function toSelection(s) {
|
|
196
|
+
return {
|
|
197
|
+
plugin: s.plugin,
|
|
198
|
+
views: s.views,
|
|
199
|
+
commands: s.commands,
|
|
200
|
+
derived: s.derived
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function journeyFiles(config) {
|
|
205
|
+
return Stdlib_Option.getOr(config.journeys, []).map(j => ({
|
|
206
|
+
group: j.group,
|
|
207
|
+
key: Stdlib_Option.getOr(j.key, journeyKey(j.group)),
|
|
208
|
+
selections: j.components.map(toSelection)
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function files(config) {
|
|
213
|
+
return [[
|
|
214
|
+
Stdlib_Option.getOr(config.key, defaultKey),
|
|
215
|
+
config.components.map(toSelection)
|
|
216
|
+
]].concat(journeyFiles(config).map(j => [
|
|
217
|
+
j.key,
|
|
218
|
+
j.selections
|
|
219
|
+
]));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function journeyUrls(config) {
|
|
223
|
+
return journeyFiles(config).map(j => [
|
|
224
|
+
j.group,
|
|
225
|
+
urlForKey(j.key)
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
|
|
195
229
|
function curate(structures, selections) {
|
|
196
230
|
return Stdlib_Result.map(Stdlib_Array.reduce(selections, {
|
|
197
231
|
TAG: "Ok",
|
|
@@ -228,6 +262,10 @@ export {
|
|
|
228
262
|
defaultKey,
|
|
229
263
|
urlForKey,
|
|
230
264
|
journeyKey,
|
|
265
|
+
toSelection,
|
|
266
|
+
journeyFiles,
|
|
267
|
+
files,
|
|
268
|
+
journeyUrls,
|
|
231
269
|
curate,
|
|
232
270
|
}
|
|
233
271
|
/* No side effect */
|
|
@@ -21,6 +21,52 @@ let toRelativePath: string => string = %raw(`function toRelativePath(moduleUrl)
|
|
|
21
21
|
}
|
|
22
22
|
}`)
|
|
23
23
|
|
|
24
|
+
// The tag of one compiled variant arm: `{TAG: "<Name>", …}` for a payload-bearing
|
|
25
|
+
// constructor, a bare string const for a payload-less one.
|
|
26
|
+
let variantTag = (variant: S.t<unknown>) =>
|
|
27
|
+
switch variant {
|
|
28
|
+
| S.Object({properties}) =>
|
|
29
|
+
switch properties->Dict.get("TAG") {
|
|
30
|
+
| Some(String({const: ?Some(tag)})) => Some(tag)
|
|
31
|
+
| _ => None
|
|
32
|
+
}
|
|
33
|
+
| S.String({const: ?Some(tag)}) => Some(tag)
|
|
34
|
+
| _ => None
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
The event schema of a DCB event log: one union over every event its slices produce.
|
|
39
|
+
|
|
40
|
+
Flattened, not nested. Every reader of an `eventLogSchemaEntry` walks exactly one
|
|
41
|
+
level — `Union` → per-variant `Object` with a `TAG` const — so a union OF unions
|
|
42
|
+
would present each slice as an arm with no tag and read as a log carrying no
|
|
43
|
+
event types at all. Arms are deduped by tag because two slices producing the same
|
|
44
|
+
event would otherwise hand sury two arms with one discriminant.
|
|
45
|
+
*/
|
|
46
|
+
let mergedEventSchema = (schemas: array<S.t<unknown>>) =>
|
|
47
|
+
switch schemas {
|
|
48
|
+
| [single] => single
|
|
49
|
+
| many =>
|
|
50
|
+
let seen = Set.make()
|
|
51
|
+
many
|
|
52
|
+
->Array.flatMap(schema =>
|
|
53
|
+
switch schema {
|
|
54
|
+
| S.Union({anyOf}) => anyOf
|
|
55
|
+
| other => [other]
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
->Array.filter(arm =>
|
|
59
|
+
switch arm->variantTag {
|
|
60
|
+
| Some(tag) if seen->Set.has(tag) => false
|
|
61
|
+
| Some(tag) =>
|
|
62
|
+
seen->Set.add(tag)
|
|
63
|
+
true
|
|
64
|
+
| None => true
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
->S.union
|
|
68
|
+
}
|
|
69
|
+
|
|
24
70
|
type dcbResult = {
|
|
25
71
|
dcbEventLogOutputs: option<DcbEventLog.outputs>,
|
|
26
72
|
stateChangeSlicesOutputs: dict<StateChangeSlice.outputs>,
|
|
@@ -1047,7 +1093,11 @@ module Make = (
|
|
|
1047
1093
|
}
|
|
1048
1094
|
})
|
|
1049
1095
|
|
|
1050
|
-
//
|
|
1096
|
+
// Every event this plugin's slices produce lands in the one DCB event log,
|
|
1097
|
+
// so the log's schema entry is the union of all of them. Taking the first
|
|
1098
|
+
// slice's schema as representative left the readers of that entry — the
|
|
1099
|
+
// upload claimer's `CLAIM_REF_FIELDS`, MCP's event-history resource —
|
|
1100
|
+
// describing one slice and silently blind to the rest.
|
|
1051
1101
|
let allProducedSchemas =
|
|
1052
1102
|
stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) =>
|
|
1053
1103
|
Sc.Spec.eventSchema->S.castToUnknown
|
|
@@ -1102,12 +1152,11 @@ module Make = (
|
|
|
1102
1152
|
->Array.concat(outboundEntries)
|
|
1103
1153
|
->Array.concat(inboundEntries),
|
|
1104
1154
|
eventLogEntries: if allProducedSchemas->Array.length > 0 {
|
|
1105
|
-
// Use the first produced event schema as representative for the event log
|
|
1106
1155
|
[
|
|
1107
1156
|
{
|
|
1108
1157
|
ReventlessInfra.Api.busKey: name ++ "DcbEventLog",
|
|
1109
1158
|
displayName: name,
|
|
1110
|
-
eventSchema: allProducedSchemas->
|
|
1159
|
+
eventSchema: allProducedSchemas->mergedEventSchema,
|
|
1111
1160
|
},
|
|
1112
1161
|
]
|
|
1113
1162
|
} else {
|
|
@@ -48,6 +48,60 @@ let toRelativePath = (function toRelativePath(moduleUrl) {
|
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
function variantTag(variant) {
|
|
52
|
+
switch (variant.type) {
|
|
53
|
+
case "string" :
|
|
54
|
+
let tag = variant.const;
|
|
55
|
+
if (tag !== undefined) {
|
|
56
|
+
return tag;
|
|
57
|
+
} else {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
case "object" :
|
|
61
|
+
let match = variant.properties["TAG"];
|
|
62
|
+
if (match === undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (match.type !== "string") {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
let tag$1 = match.const;
|
|
69
|
+
if (tag$1 !== undefined) {
|
|
70
|
+
return tag$1;
|
|
71
|
+
} else {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
default:
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function mergedEventSchema(schemas) {
|
|
80
|
+
if (schemas.length === 1) {
|
|
81
|
+
return schemas[0];
|
|
82
|
+
}
|
|
83
|
+
let seen = new Set();
|
|
84
|
+
return S.union(schemas.flatMap(schema => {
|
|
85
|
+
if (schema.type === "union") {
|
|
86
|
+
return schema.anyOf;
|
|
87
|
+
} else {
|
|
88
|
+
return [schema];
|
|
89
|
+
}
|
|
90
|
+
}).filter(arm => {
|
|
91
|
+
let tag = variantTag(arm);
|
|
92
|
+
if (tag !== undefined) {
|
|
93
|
+
if (seen.has(tag)) {
|
|
94
|
+
return false;
|
|
95
|
+
} else {
|
|
96
|
+
seen.add(tag);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
|
|
51
105
|
let emptyResult_stateChangeSlicesOutputs = {};
|
|
52
106
|
|
|
53
107
|
let emptyResult_stateViewSlicesOutputs = {};
|
|
@@ -583,7 +637,7 @@ function Make(DcbEventLogStorage) {
|
|
|
583
637
|
eventLogEntries: allProducedSchemas.length !== 0 ? [{
|
|
584
638
|
busKey: name + "DcbEventLog",
|
|
585
639
|
displayName: name,
|
|
586
|
-
eventSchema: allProducedSchemas
|
|
640
|
+
eventSchema: mergedEventSchema(allProducedSchemas)
|
|
587
641
|
}] : []
|
|
588
642
|
};
|
|
589
643
|
};
|
|
@@ -596,6 +650,8 @@ function Make(DcbEventLogStorage) {
|
|
|
596
650
|
export {
|
|
597
651
|
log,
|
|
598
652
|
toRelativePath,
|
|
653
|
+
variantTag,
|
|
654
|
+
mergedEventSchema,
|
|
599
655
|
emptyResult,
|
|
600
656
|
Make,
|
|
601
657
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// What a DCB event log tells its readers it carries.
|
|
2
|
+
//
|
|
3
|
+
// A plugin's slices all append to ONE log, but each slice declares only its own
|
|
4
|
+
// events. The log's schema entry used to be the first slice's schema, taken as
|
|
5
|
+
// representative — and every reader of that entry believed it: the upload
|
|
6
|
+
// claimer granted and untagged refs for one slice's events, and MCP described
|
|
7
|
+
// the log's history as carrying that slice's event types and no others. Nothing
|
|
8
|
+
// failed; the rest was simply invisible, which for the claimer means objects
|
|
9
|
+
// that keep their pending tag and expire while an event references them.
|
|
10
|
+
//
|
|
11
|
+
// So the assertions here are about completeness, and about the shape that
|
|
12
|
+
// completeness has to arrive in: readers walk exactly one level, `Union` →
|
|
13
|
+
// per-variant `Object` with a `TAG`, so a union of unions would be as blind as
|
|
14
|
+
// the single schema it replaced while looking like it covered everything.
|
|
15
|
+
|
|
16
|
+
open JestGlobals
|
|
17
|
+
|
|
18
|
+
let variant = (~tag: string, fields: array<(string, S.t<unknown>)>): S.t<unknown> =>
|
|
19
|
+
S.object(s => {
|
|
20
|
+
let _ = s.field("TAG", S.literal(tag)->S.castToUnknown)
|
|
21
|
+
fields->Array.forEach(((name, schema)) => {
|
|
22
|
+
let _ = s.field(name, schema)
|
|
23
|
+
})
|
|
24
|
+
()
|
|
25
|
+
})->S.castToUnknown
|
|
26
|
+
|
|
27
|
+
let ref_ = (~store: string) => Reventless.StorageRef.forStore(~store)->S.castToUnknown
|
|
28
|
+
|
|
29
|
+
let addCategory = S.union([
|
|
30
|
+
variant(~tag="CategoryAdded", [("imageUrl", ref_(~store="categoryImages"))]),
|
|
31
|
+
])->S.castToUnknown
|
|
32
|
+
|
|
33
|
+
let addProduct = S.union([
|
|
34
|
+
variant(~tag="ProductAdded", [("imageUrl", ref_(~store="productImages"))]),
|
|
35
|
+
])->S.castToUnknown
|
|
36
|
+
|
|
37
|
+
let renameCategory = S.union([
|
|
38
|
+
variant(~tag="CategoryRenamed", [("name", S.string->S.castToUnknown)]),
|
|
39
|
+
])->S.castToUnknown
|
|
40
|
+
|
|
41
|
+
describe("Dcb_Builder.mergedEventSchema", () => {
|
|
42
|
+
testSync("carries every slice's event types, not the first slice's", () => {
|
|
43
|
+
let merged = [addCategory, addProduct, renameCategory]->Dcb_Builder.mergedEventSchema
|
|
44
|
+
expect(Reventless.DcbTag.extractVariantNames(merged))->toEqual([
|
|
45
|
+
"CategoryAdded",
|
|
46
|
+
"ProductAdded",
|
|
47
|
+
"CategoryRenamed",
|
|
48
|
+
])
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// The claimer's whole input. One slice short here is a store's objects
|
|
52
|
+
// expiring under a live reference.
|
|
53
|
+
testSync("surfaces the ref fields of every slice that declares one", () => {
|
|
54
|
+
let merged = [addCategory, addProduct, renameCategory]->Dcb_Builder.mergedEventSchema
|
|
55
|
+
expect(
|
|
56
|
+
StorageRefFields.fromEventSchema(~plugin="Catalog", merged)->Array.map(e => (
|
|
57
|
+
e.eventType,
|
|
58
|
+
e.fields->Array.map(f => f.store),
|
|
59
|
+
)),
|
|
60
|
+
)->toEqual([
|
|
61
|
+
("CategoryAdded", ["Catalog.categoryImages"]),
|
|
62
|
+
("ProductAdded", ["Catalog.productImages"]),
|
|
63
|
+
])
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Two slices producing the same event would otherwise hand sury two arms
|
|
67
|
+
// under one discriminant, and every reader a duplicate.
|
|
68
|
+
testSync("keeps one arm per event type", () => {
|
|
69
|
+
let merged = [addProduct, addProduct]->Dcb_Builder.mergedEventSchema
|
|
70
|
+
expect(Reventless.DcbTag.extractVariantNames(merged))->toEqual(["ProductAdded"])
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
testSync("leaves a lone slice's schema as it is", () => {
|
|
74
|
+
let merged = [addProduct]->Dcb_Builder.mergedEventSchema
|
|
75
|
+
expect(Reventless.DcbTag.extractVariantNames(merged))->toEqual(["ProductAdded"])
|
|
76
|
+
})
|
|
77
|
+
})
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
|
+
import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
|
|
6
|
+
import * as Dcb_Builder$ReventlessCore from "../../src/components/Dcb/Dcb_Builder.res.mjs";
|
|
7
|
+
import * as StorageRefFields$ReventlessCore from "../../src/components/Api/StorageRefFields.res.mjs";
|
|
8
|
+
|
|
9
|
+
function variant(tag, fields) {
|
|
10
|
+
return S.object(s => {
|
|
11
|
+
s.f("TAG", S.literal(tag));
|
|
12
|
+
fields.forEach(param => {
|
|
13
|
+
s.f(param[0], param[1]);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function ref_(store) {
|
|
19
|
+
return StorageRef$Reventless.forStore(undefined, store);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let addCategory = S.union([variant("CategoryAdded", [[
|
|
23
|
+
"imageUrl",
|
|
24
|
+
StorageRef$Reventless.forStore(undefined, "categoryImages")
|
|
25
|
+
]])]);
|
|
26
|
+
|
|
27
|
+
let addProduct = S.union([variant("ProductAdded", [[
|
|
28
|
+
"imageUrl",
|
|
29
|
+
StorageRef$Reventless.forStore(undefined, "productImages")
|
|
30
|
+
]])]);
|
|
31
|
+
|
|
32
|
+
let renameCategory = S.union([variant("CategoryRenamed", [[
|
|
33
|
+
"name",
|
|
34
|
+
S.string
|
|
35
|
+
]])]);
|
|
36
|
+
|
|
37
|
+
globalThis.describe("Dcb_Builder.mergedEventSchema", () => {
|
|
38
|
+
globalThis.test("carries every slice's event types, not the first slice's", () => {
|
|
39
|
+
let merged = Dcb_Builder$ReventlessCore.mergedEventSchema([
|
|
40
|
+
addCategory,
|
|
41
|
+
addProduct,
|
|
42
|
+
renameCategory
|
|
43
|
+
]);
|
|
44
|
+
globalThis.expect(DcbTag$Reventless.extractVariantNames(merged)).toEqual([
|
|
45
|
+
"CategoryAdded",
|
|
46
|
+
"ProductAdded",
|
|
47
|
+
"CategoryRenamed"
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
globalThis.test("surfaces the ref fields of every slice that declares one", () => {
|
|
51
|
+
let merged = Dcb_Builder$ReventlessCore.mergedEventSchema([
|
|
52
|
+
addCategory,
|
|
53
|
+
addProduct,
|
|
54
|
+
renameCategory
|
|
55
|
+
]);
|
|
56
|
+
globalThis.expect(StorageRefFields$ReventlessCore.fromEventSchema("Catalog", merged).map(e => [
|
|
57
|
+
e.eventType,
|
|
58
|
+
e.fields.map(f => f.store)
|
|
59
|
+
])).toEqual([
|
|
60
|
+
[
|
|
61
|
+
"CategoryAdded",
|
|
62
|
+
["Catalog.categoryImages"]
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
"ProductAdded",
|
|
66
|
+
["Catalog.productImages"]
|
|
67
|
+
]
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
globalThis.test("keeps one arm per event type", () => {
|
|
71
|
+
let merged = Dcb_Builder$ReventlessCore.mergedEventSchema([
|
|
72
|
+
addProduct,
|
|
73
|
+
addProduct
|
|
74
|
+
]);
|
|
75
|
+
globalThis.expect(DcbTag$Reventless.extractVariantNames(merged)).toEqual(["ProductAdded"]);
|
|
76
|
+
});
|
|
77
|
+
globalThis.test("leaves a lone slice's schema as it is", () => {
|
|
78
|
+
let merged = Dcb_Builder$ReventlessCore.mergedEventSchema([addProduct]);
|
|
79
|
+
globalThis.expect(DcbTag$Reventless.extractVariantNames(merged)).toEqual(["ProductAdded"]);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
variant,
|
|
85
|
+
ref_,
|
|
86
|
+
addCategory,
|
|
87
|
+
addProduct,
|
|
88
|
+
renameCategory,
|
|
89
|
+
}
|
|
90
|
+
/* addCategory Not a pure module */
|