@reventlessdev/reventless-core 3.0.0-alpha.258 → 3.0.0-alpha.260
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 +18 -0
- package/package.json +9 -9
- 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 +16 -2
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +13 -5
- package/src/components/Api/SchemaType.res +81 -1
- package/src/components/Api/SchemaType.res.mjs +82 -3
- package/src/components/Api/SuryToJsonSchema.res +10 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +135 -110
- 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 +91 -3
- package/tests/api/SuryToJsonSchemaTest.res.mjs +66 -4
- package/tests/plugin/PluginStructureAccessTest.res.mjs +3 -3
- package/tests/plugin/PluginStructureTest.res +24 -5
- package/tests/plugin/PluginStructureTest.res.mjs +32 -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 +4 -2
- package/tests/plugin/{StateViewSlice → StateView}/PsAnnotatedView.res.mjs +8 -4
- 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}/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
|
@@ -10,6 +10,13 @@ type rec schemaType =
|
|
|
10
10
|
| ScalarBigInt
|
|
11
11
|
| EntityId
|
|
12
12
|
| DateTime
|
|
13
|
+
| /** A calendar day, with no instant in it. A shape of its own rather than a
|
|
14
|
+
plain string so `format: "date"` reaches the JSON Schema — but, unlike
|
|
15
|
+
`DateTime`, it is not excluded from the generic semantic wrapper below,
|
|
16
|
+
so it carries `x-reventless-semantic` as well. `DateTime`'s exclusion
|
|
17
|
+
exists because its bare-`format` output predates the marker and is a
|
|
18
|
+
published contract; a new semantic has no such history. */
|
|
19
|
+
CalendarDate
|
|
13
20
|
| Nullable(schemaType)
|
|
14
21
|
| ArrayOf(schemaType)
|
|
15
22
|
| ObjectRef(string, dict<schemaType>)
|
|
@@ -20,6 +27,10 @@ type rec schemaType =
|
|
|
20
27
|
are *not* expressed this way: they long predate the generic marker and
|
|
21
28
|
their JSON Schema `format` output is a published contract. */
|
|
22
29
|
Semantic(Reventless.Semantic.t, schemaType)
|
|
30
|
+
| /** A field whose type declares the value it opens with. Wraps the shape the
|
|
31
|
+
value has, as `Semantic` does, so a consumer that cares only about shape
|
|
32
|
+
unwraps: a default changes where a form starts, never what the field is. */
|
|
33
|
+
Defaulted(JSON.t, schemaType)
|
|
23
34
|
| /** A variant used as a field: the union's name, and one arm per constructor
|
|
24
35
|
keyed by the `TAG` sury discriminates on. Each arm is the `ObjectRef` its
|
|
25
36
|
member type is emitted from, so the arm's own name travels with it and
|
|
@@ -32,6 +43,7 @@ type rec schemaType =
|
|
|
32
43
|
|
|
33
44
|
let isTagged = Reventless.DcbTag.isTagged
|
|
34
45
|
let isDateTime = Reventless.DateTime.isDateTime
|
|
46
|
+
let isCalendarDate = Reventless.CalendarDate.isCalendarDate
|
|
35
47
|
|
|
36
48
|
let isIdFieldName = (name: string): bool => {
|
|
37
49
|
let lower = String.toLowerCase(name)
|
|
@@ -74,6 +86,17 @@ let canonicalName = (id: string): option<string> =>
|
|
|
74
86
|
->Option.map(((_, name)) => name)
|
|
75
87
|
|
|
76
88
|
let rec fromSury = (~parentName: string, ~fieldName: string, schema: S.t<unknown>): schemaType => {
|
|
89
|
+
// Outermost, so it wraps whatever the field turns out to be, a semantic
|
|
90
|
+
// composite included — the other order buries the marker under a shape whose
|
|
91
|
+
// consumers unwrap it.
|
|
92
|
+
let shape = withoutDefault(~parentName, ~fieldName, schema)
|
|
93
|
+
switch Reventless.FieldDefault.getFrom(schema) {
|
|
94
|
+
| Some(value) => Defaulted(value, shape)
|
|
95
|
+
| None => shape
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
and withoutDefault = (~parentName: string, ~fieldName: string, schema: S.t<unknown>): schemaType => {
|
|
77
100
|
// Semantics the IR already has a dedicated shape for keep it: `dateTime` and
|
|
78
101
|
// `reference` are read below via `isDateTime` / `getTarget`, both of which now
|
|
79
102
|
// consult the generic marker, and their `format` output is a published
|
|
@@ -107,6 +130,7 @@ and shapeOf = (~parentName: string, ~fieldName: string, schema: S.t<unknown>): s
|
|
|
107
130
|
switch schema {
|
|
108
131
|
| String({const: ?Some(_)}) => ScalarString
|
|
109
132
|
| String(_) if isDateTime(schema) => DateTime
|
|
133
|
+
| String(_) if isCalendarDate(schema) => CalendarDate
|
|
110
134
|
| String(_) => isIdFieldName(fieldName) ? EntityId : ScalarString
|
|
111
135
|
// `Int32` and `Port` are both whole-number formats; anything else (or no
|
|
112
136
|
// format at all) is a `float` as far as the schema is concerned.
|
|
@@ -348,15 +372,71 @@ let optionalFieldNames = (schema: S.t<unknown>): array<string> =>
|
|
|
348
372
|
| _ => []
|
|
349
373
|
}
|
|
350
374
|
|
|
375
|
+
// A lifecycle trail's entries are `{state, at}`, and `state` holds the record's
|
|
376
|
+
// own lifecycle. Walked under the lifecycle field's name so it resolves to the
|
|
377
|
+
// enum that field already emits, rather than a second copy under the trail's
|
|
378
|
+
// path — the trail and the current state must read as one vocabulary.
|
|
379
|
+
let trailShape = (
|
|
380
|
+
~parentName: string,
|
|
381
|
+
~trailField: string,
|
|
382
|
+
~lifecycleField: string,
|
|
383
|
+
schema: S.t<unknown>,
|
|
384
|
+
): schemaType => {
|
|
385
|
+
let entryName =
|
|
386
|
+
parentName ++
|
|
387
|
+
trailField->String.charAt(0)->String.toUpperCase ++
|
|
388
|
+
trailField->String.slice(~start=1, ~end=trailField->String.length)
|
|
389
|
+
let entrySchema = switch schema {
|
|
390
|
+
| Array({additionalItems: Schema(item)}) => Some(item)
|
|
391
|
+
| _ => None
|
|
392
|
+
}
|
|
393
|
+
switch entrySchema {
|
|
394
|
+
| Some(Object({properties})) =>
|
|
395
|
+
let fields = Dict.make()
|
|
396
|
+
properties
|
|
397
|
+
->Dict.toArray
|
|
398
|
+
->Array.forEach(((propName, propSchema)) =>
|
|
399
|
+
fields->Dict.set(
|
|
400
|
+
propName,
|
|
401
|
+
propName === "state"
|
|
402
|
+
? fromSury(~parentName, ~fieldName=lifecycleField, propSchema)
|
|
403
|
+
: fromSury(~parentName=entryName, ~fieldName=propName, propSchema),
|
|
404
|
+
)
|
|
405
|
+
)
|
|
406
|
+
let shape = ArrayOf(ObjectRef(entryName, fields))
|
|
407
|
+
// Walking the entries by shape skips the semantic dispatch `fromSury` does,
|
|
408
|
+
// so the marker is re-applied here. Without it the trail reaches a consumer
|
|
409
|
+
// as an unremarkable array of objects, and recognising it would come down to
|
|
410
|
+
// the field's name.
|
|
411
|
+
switch Reventless.Semantic.get(schema) {
|
|
412
|
+
| Some(sem) => Semantic(sem, shape)
|
|
413
|
+
| None => shape
|
|
414
|
+
}
|
|
415
|
+
| _ => fromSury(~parentName, ~fieldName=trailField, schema)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
351
419
|
let fromSuryObject = (~typeName: string, schema: S.t<unknown>): option<dict<schemaType>> =>
|
|
352
420
|
switch schema {
|
|
353
421
|
| Object({properties}) =>
|
|
422
|
+
let trail = switch (
|
|
423
|
+
Reventless.Lifecycle.Trail.fieldName(schema),
|
|
424
|
+
Reventless.Lifecycle.fieldName(schema),
|
|
425
|
+
) {
|
|
426
|
+
| (Some(trailField), Some(lifecycleField)) => Some((trailField, lifecycleField))
|
|
427
|
+
| _ => None
|
|
428
|
+
}
|
|
354
429
|
let fields = Dict.make()
|
|
355
430
|
properties
|
|
356
431
|
->Dict.toArray
|
|
357
432
|
->Array.forEach(((propName, propSchema)) => {
|
|
358
433
|
if propName !== "TAG" {
|
|
359
|
-
|
|
434
|
+
let shape = switch trail {
|
|
435
|
+
| Some((trailField, lifecycleField)) if trailField === propName =>
|
|
436
|
+
trailShape(~parentName=typeName, ~trailField, ~lifecycleField, propSchema)
|
|
437
|
+
| _ => fromSury(~parentName=typeName, ~fieldName=propName, propSchema)
|
|
438
|
+
}
|
|
439
|
+
fields->Dict.set(propName, shape)
|
|
360
440
|
}
|
|
361
441
|
})
|
|
362
442
|
Some(fields)
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
5
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
6
|
-
import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/
|
|
6
|
+
import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/semantic/DateTime.res.mjs";
|
|
7
7
|
import * as Semantic$Reventless from "@reventlessdev/reventless-spec/src/semantic/Semantic.res.mjs";
|
|
8
|
+
import * as Lifecycle$Reventless from "@reventlessdev/reventless-spec/src/types/Lifecycle.res.mjs";
|
|
8
9
|
import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
|
|
9
10
|
import * as TaggedUnion$Reventless from "@reventlessdev/reventless-spec/src/components/TaggedUnion.res.mjs";
|
|
11
|
+
import * as CalendarDate$Reventless from "@reventlessdev/reventless-spec/src/semantic/CalendarDate.res.mjs";
|
|
12
|
+
import * as FieldDefault$Reventless from "@reventlessdev/reventless-spec/src/components/FieldDefault.res.mjs";
|
|
10
13
|
|
|
11
14
|
function isIdFieldName(name) {
|
|
12
15
|
let lower = name.toLowerCase();
|
|
@@ -52,6 +55,20 @@ function canonicalName(id) {
|
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
function fromSury(parentName, fieldName, schema) {
|
|
58
|
+
let shape = withoutDefault(parentName, fieldName, schema);
|
|
59
|
+
let value = FieldDefault$Reventless.getFrom(schema);
|
|
60
|
+
if (value !== undefined) {
|
|
61
|
+
return {
|
|
62
|
+
TAG: "Defaulted",
|
|
63
|
+
_0: value,
|
|
64
|
+
_1: shape
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
return shape;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function withoutDefault(parentName, fieldName, schema) {
|
|
55
72
|
let sem = Semantic$Reventless.get(schema);
|
|
56
73
|
if (sem === undefined) {
|
|
57
74
|
return shapeOf(parentName, fieldName, schema);
|
|
@@ -79,6 +96,8 @@ function shapeOf(parentName, fieldName, schema) {
|
|
|
79
96
|
return "ScalarString";
|
|
80
97
|
} else if (DateTime$Reventless.isDateTime(schema)) {
|
|
81
98
|
return "DateTime";
|
|
99
|
+
} else if (CalendarDate$Reventless.isCalendarDate(schema)) {
|
|
100
|
+
return "CalendarDate";
|
|
82
101
|
} else if (isIdFieldName(fieldName)) {
|
|
83
102
|
return "EntityId";
|
|
84
103
|
} else {
|
|
@@ -324,17 +343,72 @@ function optionalFieldNames(schema) {
|
|
|
324
343
|
}
|
|
325
344
|
}
|
|
326
345
|
|
|
346
|
+
function trailShape(parentName, trailField, lifecycleField, schema) {
|
|
347
|
+
let entryName = parentName + trailField.charAt(0).toUpperCase() + trailField.slice(1, trailField.length);
|
|
348
|
+
let entrySchema;
|
|
349
|
+
if (schema.type === "array") {
|
|
350
|
+
let item = schema.additionalItems;
|
|
351
|
+
entrySchema = item === "strip" || item === "strict" ? undefined : item;
|
|
352
|
+
} else {
|
|
353
|
+
entrySchema = undefined;
|
|
354
|
+
}
|
|
355
|
+
if (entrySchema === undefined) {
|
|
356
|
+
return fromSury(parentName, trailField, schema);
|
|
357
|
+
}
|
|
358
|
+
if (entrySchema.type !== "object") {
|
|
359
|
+
return fromSury(parentName, trailField, schema);
|
|
360
|
+
}
|
|
361
|
+
let fields = {};
|
|
362
|
+
Object.entries(entrySchema.properties).forEach(param => {
|
|
363
|
+
let propSchema = param[1];
|
|
364
|
+
let propName = param[0];
|
|
365
|
+
fields[propName] = propName === "state" ? fromSury(parentName, lifecycleField, propSchema) : fromSury(entryName, propName, propSchema);
|
|
366
|
+
});
|
|
367
|
+
let shape = {
|
|
368
|
+
TAG: "ArrayOf",
|
|
369
|
+
_0: {
|
|
370
|
+
TAG: "ObjectRef",
|
|
371
|
+
_0: entryName,
|
|
372
|
+
_1: fields
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
let sem = Semantic$Reventless.get(schema);
|
|
376
|
+
if (sem !== undefined) {
|
|
377
|
+
return {
|
|
378
|
+
TAG: "Semantic",
|
|
379
|
+
_0: sem,
|
|
380
|
+
_1: shape
|
|
381
|
+
};
|
|
382
|
+
} else {
|
|
383
|
+
return shape;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
327
387
|
function fromSuryObject(typeName, schema) {
|
|
328
388
|
if (schema.type !== "object") {
|
|
329
389
|
return;
|
|
330
390
|
}
|
|
391
|
+
let match = Lifecycle$Reventless.Trail.fieldName(schema);
|
|
392
|
+
let match$1 = Lifecycle$Reventless.fieldName(schema);
|
|
393
|
+
let trail = match !== undefined && match$1 !== undefined ? [
|
|
394
|
+
match,
|
|
395
|
+
match$1
|
|
396
|
+
] : undefined;
|
|
331
397
|
let fields = {};
|
|
332
398
|
Object.entries(schema.properties).forEach(param => {
|
|
333
399
|
let propName = param[0];
|
|
334
|
-
if (propName
|
|
335
|
-
fields[propName] = fromSury(typeName, propName, param[1]);
|
|
400
|
+
if (propName === "TAG") {
|
|
336
401
|
return;
|
|
337
402
|
}
|
|
403
|
+
let propSchema = param[1];
|
|
404
|
+
let shape;
|
|
405
|
+
if (trail !== undefined) {
|
|
406
|
+
let trailField = trail[0];
|
|
407
|
+
shape = trailField === propName ? trailShape(typeName, trailField, trail[1], propSchema) : fromSury(typeName, propName, propSchema);
|
|
408
|
+
} else {
|
|
409
|
+
shape = fromSury(typeName, propName, propSchema);
|
|
410
|
+
}
|
|
411
|
+
fields[propName] = shape;
|
|
338
412
|
});
|
|
339
413
|
return fields;
|
|
340
414
|
}
|
|
@@ -343,21 +417,26 @@ let isTagged = DcbTag$Reventless.isTagged;
|
|
|
343
417
|
|
|
344
418
|
let isDateTime = DateTime$Reventless.isDateTime;
|
|
345
419
|
|
|
420
|
+
let isCalendarDate = CalendarDate$Reventless.isCalendarDate;
|
|
421
|
+
|
|
346
422
|
let maxReportDepth = 12;
|
|
347
423
|
|
|
348
424
|
export {
|
|
349
425
|
isTagged,
|
|
350
426
|
isDateTime,
|
|
427
|
+
isCalendarDate,
|
|
351
428
|
isIdFieldName,
|
|
352
429
|
isIdsFieldName,
|
|
353
430
|
semanticCompositeNames,
|
|
354
431
|
canonicalName,
|
|
355
432
|
fromSury,
|
|
433
|
+
withoutDefault,
|
|
356
434
|
shapeOf,
|
|
357
435
|
maxReportDepth,
|
|
358
436
|
collectUnclassifiedUnions,
|
|
359
437
|
unclassifiedUnions,
|
|
360
438
|
optionalFieldNames,
|
|
439
|
+
trailShape,
|
|
361
440
|
fromSuryObject,
|
|
362
441
|
}
|
|
363
442
|
/* DcbTag-Reventless Not a pure module */
|
|
@@ -207,6 +207,7 @@ let rec fromSchemaType = (~inputNames: bool=false, st: SchemaType.schemaType): J
|
|
|
207
207
|
| ScalarBigInt => jsonObject([("type", str("integer"))])
|
|
208
208
|
| EntityId => jsonObject([("type", str("string")), ("format", str("uuid"))])
|
|
209
209
|
| DateTime => jsonObject([("type", str("string")), ("format", str("date-time"))])
|
|
210
|
+
| CalendarDate => jsonObject([("type", str("string")), ("format", str("date"))])
|
|
210
211
|
| Nullable(inner) =>
|
|
211
212
|
let innerSchema = fromSchemaType(~inputNames, inner)
|
|
212
213
|
jsonObject([("oneOf", JSON.Encode.array([innerSchema, jsonObject([("type", str("null"))])]))])
|
|
@@ -233,6 +234,15 @@ let rec fromSchemaType = (~inputNames: bool=false, st: SchemaType.schemaType): J
|
|
|
233
234
|
| _ => base
|
|
234
235
|
}
|
|
235
236
|
base->withSemantic(sem)
|
|
237
|
+
// JSON Schema's own keyword, not an `x-reventless-` extension: the vocabulary
|
|
238
|
+
// already has a word for this one, so any client reads it.
|
|
239
|
+
| Defaulted(value, inner) =>
|
|
240
|
+
switch fromSchemaType(~inputNames, inner)->JSON.Decode.object {
|
|
241
|
+
| Some(obj) =>
|
|
242
|
+
obj->Dict.set("default", value)
|
|
243
|
+
JSON.Encode.object(obj)
|
|
244
|
+
| None => fromSchemaType(~inputNames, inner)
|
|
245
|
+
}
|
|
236
246
|
// A union has no input form — see the fragment generator, which emits `String`
|
|
237
247
|
// and says so — hence no name to publish on either arm.
|
|
238
248
|
// A union is `oneOf` its arms, each an object carrying the `TAG` const it is
|
|
@@ -170,119 +170,144 @@ function withGraphqlInput(schema, name) {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
function fromSchemaType(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
]
|
|
203
|
-
]);
|
|
204
|
-
case "DateTime" :
|
|
205
|
-
return Object.fromEntries([
|
|
206
|
-
[
|
|
207
|
-
"type",
|
|
208
|
-
"string"
|
|
209
|
-
],
|
|
210
|
-
[
|
|
211
|
-
"format",
|
|
212
|
-
"date-time"
|
|
213
|
-
]
|
|
214
|
-
]);
|
|
215
|
-
case "ScalarString" :
|
|
216
|
-
case "Unknown" :
|
|
217
|
-
return Object.fromEntries([[
|
|
218
|
-
"type",
|
|
219
|
-
"string"
|
|
220
|
-
]]);
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
switch (st.TAG) {
|
|
224
|
-
case "Nullable" :
|
|
225
|
-
let innerSchema = fromSchemaType(inputNames, st._0);
|
|
226
|
-
return Object.fromEntries([[
|
|
227
|
-
"oneOf",
|
|
173
|
+
function fromSchemaType(_inputNamesOpt, _st) {
|
|
174
|
+
while (true) {
|
|
175
|
+
let inputNamesOpt = _inputNamesOpt;
|
|
176
|
+
let st = _st;
|
|
177
|
+
let inputNames = inputNamesOpt !== undefined ? inputNamesOpt : false;
|
|
178
|
+
if (typeof st !== "object") {
|
|
179
|
+
switch (st) {
|
|
180
|
+
case "ScalarNumber" :
|
|
181
|
+
return Object.fromEntries([[
|
|
182
|
+
"type",
|
|
183
|
+
"number"
|
|
184
|
+
]]);
|
|
185
|
+
case "ScalarBoolean" :
|
|
186
|
+
return Object.fromEntries([[
|
|
187
|
+
"type",
|
|
188
|
+
"boolean"
|
|
189
|
+
]]);
|
|
190
|
+
case "ScalarInt" :
|
|
191
|
+
case "ScalarBigInt" :
|
|
192
|
+
return Object.fromEntries([[
|
|
193
|
+
"type",
|
|
194
|
+
"integer"
|
|
195
|
+
]]);
|
|
196
|
+
case "EntityId" :
|
|
197
|
+
return Object.fromEntries([
|
|
198
|
+
[
|
|
199
|
+
"type",
|
|
200
|
+
"string"
|
|
201
|
+
],
|
|
228
202
|
[
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
"type",
|
|
232
|
-
"null"
|
|
233
|
-
]])
|
|
203
|
+
"format",
|
|
204
|
+
"uuid"
|
|
234
205
|
]
|
|
235
|
-
]
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
[
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
206
|
+
]);
|
|
207
|
+
case "DateTime" :
|
|
208
|
+
return Object.fromEntries([
|
|
209
|
+
[
|
|
210
|
+
"type",
|
|
211
|
+
"string"
|
|
212
|
+
],
|
|
213
|
+
[
|
|
214
|
+
"format",
|
|
215
|
+
"date-time"
|
|
216
|
+
]
|
|
217
|
+
]);
|
|
218
|
+
case "CalendarDate" :
|
|
219
|
+
return Object.fromEntries([
|
|
220
|
+
[
|
|
221
|
+
"type",
|
|
222
|
+
"string"
|
|
223
|
+
],
|
|
224
|
+
[
|
|
225
|
+
"format",
|
|
226
|
+
"date"
|
|
227
|
+
]
|
|
228
|
+
]);
|
|
229
|
+
case "ScalarString" :
|
|
230
|
+
case "Unknown" :
|
|
231
|
+
return Object.fromEntries([[
|
|
232
|
+
"type",
|
|
233
|
+
"string"
|
|
234
|
+
]]);
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
switch (st.TAG) {
|
|
238
|
+
case "Nullable" :
|
|
239
|
+
let innerSchema = fromSchemaType(inputNames, st._0);
|
|
240
|
+
return Object.fromEntries([[
|
|
241
|
+
"oneOf",
|
|
242
|
+
[
|
|
243
|
+
innerSchema,
|
|
244
|
+
Object.fromEntries([[
|
|
245
|
+
"type",
|
|
246
|
+
"null"
|
|
247
|
+
]])
|
|
248
|
+
]
|
|
249
|
+
]]);
|
|
250
|
+
case "ArrayOf" :
|
|
251
|
+
return Object.fromEntries([
|
|
252
|
+
[
|
|
253
|
+
"type",
|
|
254
|
+
"array"
|
|
255
|
+
],
|
|
256
|
+
[
|
|
257
|
+
"items",
|
|
258
|
+
fromSchemaType(inputNames, st._0)
|
|
259
|
+
]
|
|
260
|
+
]);
|
|
261
|
+
case "ObjectRef" :
|
|
262
|
+
let base = objectRefToJsonSchema(undefined, undefined, undefined, undefined, inputNames, st._1);
|
|
263
|
+
if (inputNames) {
|
|
264
|
+
return withGraphqlInput(base, st._0);
|
|
265
|
+
} else {
|
|
266
|
+
return base;
|
|
267
|
+
}
|
|
268
|
+
case "Enum" :
|
|
269
|
+
return Object.fromEntries([
|
|
270
|
+
[
|
|
271
|
+
"type",
|
|
272
|
+
"string"
|
|
273
|
+
],
|
|
274
|
+
[
|
|
275
|
+
"enum",
|
|
276
|
+
st._1.map(prim => prim)
|
|
277
|
+
]
|
|
278
|
+
]);
|
|
279
|
+
case "Semantic" :
|
|
280
|
+
let inner = st._1;
|
|
281
|
+
let sem = st._0;
|
|
282
|
+
let base$1 = fromSchemaType(inputNames, inner);
|
|
283
|
+
let match = SchemaType$ReventlessCore.canonicalName(sem.id);
|
|
284
|
+
let base$2 = inputNames && match !== undefined && typeof inner === "object" && inner.TAG === "ObjectRef" ? withGraphqlInput(base$1, match + "Input") : base$1;
|
|
285
|
+
return withSemantic(base$2, sem);
|
|
286
|
+
case "Defaulted" :
|
|
287
|
+
let inner$1 = st._1;
|
|
288
|
+
let obj = Stdlib_JSON.Decode.object(fromSchemaType(inputNames, inner$1));
|
|
289
|
+
if (obj !== undefined) {
|
|
290
|
+
obj["default"] = st._0;
|
|
291
|
+
return obj;
|
|
292
|
+
}
|
|
293
|
+
_st = inner$1;
|
|
294
|
+
_inputNamesOpt = inputNames;
|
|
295
|
+
continue;
|
|
296
|
+
case "TaggedUnion" :
|
|
297
|
+
let members = st._1.map(param => armToJsonSchema(inputNames, param[0], param[1]));
|
|
298
|
+
return Object.fromEntries([
|
|
299
|
+
[
|
|
300
|
+
"oneOf",
|
|
301
|
+
members
|
|
302
|
+
],
|
|
303
|
+
[
|
|
304
|
+
"x-reventless-union",
|
|
305
|
+
st._0
|
|
306
|
+
]
|
|
307
|
+
]);
|
|
308
|
+
}
|
|
284
309
|
}
|
|
285
|
-
}
|
|
310
|
+
};
|
|
286
311
|
}
|
|
287
312
|
|
|
288
313
|
function armToJsonSchema(inputNamesOpt, tag, armType) {
|
|
@@ -31,13 +31,16 @@ module Make = (
|
|
|
31
31
|
json,
|
|
32
32
|
)} actions:${actionsStr}`,
|
|
33
33
|
)->Effect.runSync
|
|
34
|
-
|
|
34
|
+
// Rewritten here rather than at the apply below: a trail entry is
|
|
35
|
+
// stamped with this envelope's own time.
|
|
36
|
+
actions->Array.map(
|
|
37
|
+
Projection.rewriteAction(_, ~at=context.meta.time, ReadModelSpec.stateSchema),
|
|
38
|
+
)
|
|
35
39
|
})
|
|
36
40
|
->Array.flat
|
|
37
41
|
->Array.reduce(Effect.succeed(), (acc, action) =>
|
|
38
42
|
acc->Effect.flatMap(
|
|
39
43
|
_ => {
|
|
40
|
-
let action = Projection.rewriteAction(action, ReadModelSpec.stateSchema)
|
|
41
44
|
Effect.promise(
|
|
42
45
|
() => Projection.handleAction(~comp, action, Spec.operations, ReadModelSpec.subIdConfig),
|
|
43
46
|
)->Effect.map(_ => ())
|
|
@@ -28,11 +28,8 @@ function Make(ReadModelSpec) {
|
|
|
28
28
|
let actionsStr = LogFormat$ReventlessCore.actionNames(actions);
|
|
29
29
|
let idxStr = (idx + 1 | 0).toString();
|
|
30
30
|
Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, json, `handling event ` + idxStr + `/` + total + ` from ` + sourceName + `: ` + LogFormat$ReventlessCore.eventDetail(json) + ` actions:` + actionsStr));
|
|
31
|
-
return actions;
|
|
32
|
-
}).flat(), Effect.succeed(), (acc, action) => Effect.flatMap(acc, () => {
|
|
33
|
-
let action$1 = Projection$ReventlessCore.rewriteAction(action, ReadModelSpec.stateSchema);
|
|
34
|
-
return Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(comp, action$1, Spec.operations, ReadModelSpec.subIdConfig)), param => {});
|
|
35
|
-
}));
|
|
31
|
+
return actions.map(__x => Projection$ReventlessCore.rewriteAction(__x, context.meta.time, ReadModelSpec.stateSchema));
|
|
32
|
+
}).flat(), Effect.succeed(), (acc, action) => Effect.flatMap(acc, () => Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(comp, action, Spec.operations, ReadModelSpec.subIdConfig)), param => {})));
|
|
36
33
|
});
|
|
37
34
|
return {
|
|
38
35
|
EventProjector: EventProjector,
|
|
@@ -148,18 +148,19 @@ module Make = (
|
|
|
148
148
|
~detail=eventData,
|
|
149
149
|
`handling event ${idxStr}/${total}: ${LogFormat.bold(eventType)}${fieldsStr} ${actionsStr}`,
|
|
150
150
|
)->Effect.runSync
|
|
151
|
-
|
|
151
|
+
// A slice's state carries the same synthetic `displayName` the
|
|
152
|
+
// ppx injects for a read model's, and the same lifecycle trail.
|
|
153
|
+
// Rewritten here rather than at the apply below because the
|
|
154
|
+
// trail entry is stamped with this envelope's own time.
|
|
155
|
+
actions->Array.map(
|
|
156
|
+
FrameworkProjection.rewriteAction(_, ~at=meta.time, Spec.stateSchema),
|
|
157
|
+
)
|
|
152
158
|
| None => []
|
|
153
159
|
}
|
|
154
160
|
})
|
|
155
161
|
->Array.flat
|
|
156
162
|
->Array.reduce(Effect.succeed(), (acc, action) =>
|
|
157
163
|
acc->Effect.flatMap(_ => {
|
|
158
|
-
// A slice's state carries the same synthetic `displayName` the
|
|
159
|
-
// ppx injects for a read model's, so it is composed on the same
|
|
160
|
-
// terms — without this the column stays null and every surface
|
|
161
|
-
// names the row by its id.
|
|
162
|
-
let action = FrameworkProjection.rewriteAction(action, Spec.stateSchema)
|
|
163
164
|
Effect.promise(() =>
|
|
164
165
|
FrameworkProjection.handleAction(~comp, action, projectionOps, Spec.subIdConfig)
|
|
165
166
|
)->Effect.map(_ => ())
|
|
@@ -120,11 +120,8 @@ function Make(RuntimeEnvironment) {
|
|
|
120
120
|
let f = Object.entries(dataDict).map(param => param[0] + `:` + JSON.stringify(param[1])).join(",");
|
|
121
121
|
let fieldsStr = f === "" ? "" : `({` + f + `})`;
|
|
122
122
|
Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, dataDict, `handling event ` + idxStr + `/` + total + `: ` + LogFormat$ReventlessCore.bold(eventType) + fieldsStr + ` ` + actionsStr));
|
|
123
|
-
return actions;
|
|
124
|
-
}).flat(), Effect.succeed(), (acc, action) => Effect.flatMap(acc, () => {
|
|
125
|
-
let action$1 = Projection$ReventlessCore.rewriteAction(action, Spec.stateSchema);
|
|
126
|
-
return Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(comp, action$1, projectionOps, Spec.subIdConfig)), param => {});
|
|
127
|
-
}));
|
|
123
|
+
return actions.map(__x => Projection$ReventlessCore.rewriteAction(__x, meta.time, Spec.stateSchema));
|
|
124
|
+
}).flat(), Effect.succeed(), (acc, action) => Effect.flatMap(acc, () => Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(comp, action, projectionOps, Spec.subIdConfig)), param => {})));
|
|
128
125
|
});
|
|
129
126
|
let handler = SpecificEventCollector.makeHandler(ec, jsonEventsHandler);
|
|
130
127
|
let resources = Component$ReventlessCore.outputs(queryDb).resources;
|
|
@@ -48,7 +48,9 @@ module Make = (
|
|
|
48
48
|
`handling event ${idx.contents->Int.toString}/${count}: ${LogFormat.bold(raw.eventType)}(${id}) ${actionsStr}`,
|
|
49
49
|
)->Effect.runSync
|
|
50
50
|
allActions->Array.pushMany(
|
|
51
|
-
actions->Array.map(
|
|
51
|
+
actions->Array.map(
|
|
52
|
+
FrameworkProjection.rewriteAction(_, ~at=raw.meta.time, Spec.stateSchema),
|
|
53
|
+
),
|
|
52
54
|
)
|
|
53
55
|
Some(event)
|
|
54
56
|
| None => None
|
|
@@ -35,7 +35,7 @@ function Make(Spec) {
|
|
|
35
35
|
});
|
|
36
36
|
let actionsStr = LogFormat$ReventlessCore.actionNames(actions);
|
|
37
37
|
Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, raw.data, `handling event ` + idx.contents.toString() + `/` + count + `: ` + LogFormat$ReventlessCore.bold(raw.eventType) + `(` + id + `) ` + actionsStr));
|
|
38
|
-
allActions.push(...actions.map(__x => Projection$ReventlessCore.rewriteAction(__x, Spec.stateSchema)));
|
|
38
|
+
allActions.push(...actions.map(__x => Projection$ReventlessCore.rewriteAction(__x, raw.meta.time, Spec.stateSchema)));
|
|
39
39
|
return Primitive_option.some(event);
|
|
40
40
|
});
|
|
41
41
|
let skipped = rawEvents.length - events.length | 0;
|