@reventlessdev/reventless-core 3.0.0-alpha.236 → 3.0.0-alpha.238
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 +23 -0
- package/package.json +6 -6
- package/src/Message.res +1 -1
- package/src/adapter/Monitoring/Monitoring.res +1 -1
- package/src/admin/Platform_Admin_Structure.res +5 -1
- package/src/admin/Platform_Admin_Structure.res.mjs +4 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res +4 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +11 -3
- package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res +1 -1
- package/src/components/Aggregate/Aggregate_Callback.res +2 -2
- package/src/components/Api/ApiAllowedStatesHelpers.res +2 -3
- package/src/components/Api/ApiTargetStateHelpers.res +4 -3
- package/src/components/Api/GraphQL_FragmentGenerator.res +14 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +1 -1
- package/src/components/Api/QueryDbListQuery.res +25 -1
- package/src/components/Api/QueryDbListQuery.res.mjs +11 -4
- package/src/components/Api/SuryToJsonSchema.res +39 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +34 -3
- package/src/components/Dcb/Dcb_Builder.res +79 -16
- package/src/components/Dcb/Dcb_Builder.res.mjs +41 -5
- package/src/components/EventLog/EventLog.res +1 -1
- package/src/plugin/component/Plugin_Structure.res +377 -18
- package/src/plugin/component/Plugin_Structure.res.mjs +204 -9
- package/src/plugin/connect/PluginExtensionPoint_UiFragment.res +1 -1
- package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +3 -2
- package/tests/admin/Platform_Admin_StructureTest.res +49 -0
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +24 -0
- package/tests/admin/Platform_BakedManifestTest.res +3 -1
- package/tests/admin/Platform_BakedManifestTest.res.mjs +3 -1
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +14 -8
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +24 -12
- package/tests/admin/Platform_PluginStructuresApiTest.res +3 -1
- package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +6 -2
- package/tests/aggregate/AggregateCacheTest.res +1 -1
- package/tests/aggregate/AggregateSnapshotTest.res +1 -1
- package/tests/api/Api_IdsTest.res.mjs +1 -1
- package/tests/api/SuryToJsonSchemaTest.res +141 -1
- package/tests/api/SuryToJsonSchemaTest.res.mjs +269 -54
- package/tests/commandgenerator/OwnerStampingTest.res +57 -0
- package/tests/commandgenerator/OwnerStampingTest.res.mjs +19 -0
- package/tests/message/MessageTest.res +1 -1
- package/tests/plugin/HeartbeatDisconnectGraceTest.res +1 -1
- package/tests/plugin/PluginStructureTest.res +332 -15
- package/tests/plugin/PluginStructureTest.res.mjs +316 -10
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res +31 -0
- package/tests/plugin/StateChangeSlice/PsDispatchShipment.res.mjs +73 -0
- package/tests/plugin/StateChangeSlice/PsShipOrder.res +16 -1
- package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +7 -2
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +3 -2
- package/tests/plugin/StateViewSlice/PsShipmentsView.res +26 -0
- package/tests/plugin/StateViewSlice/PsShipmentsView.res.mjs +101 -0
- package/tests/plugin/pluginDefinitionRequiredScalars.txt +2 -0
- package/tests/util/CsvStreamTest.res +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Step 5 of docs/plans/aggregate-snapshotting.md — the Aggregate_Callback
|
|
1
|
+
// Step 5 of docs/plans/done/aggregate-snapshotting.md — the Aggregate_Callback
|
|
2
2
|
// snapshot wiring: a snapshot-enabled behavior seeds cold replays from the
|
|
3
3
|
// latest persisted snapshot (hash-gated) and writes a fresh one every
|
|
4
4
|
// `interval` events, fire-and-forget. Snapshots never affect correctness — the
|
|
@@ -77,7 +77,7 @@ globalThis.describe("filter.ids matches whichever form each side holds", () => {
|
|
|
77
77
|
"ids",
|
|
78
78
|
filterIds.map(prim => prim)
|
|
79
79
|
]]);
|
|
80
|
-
let conn = QueryDbListQuery$ReventlessCore.run([row(rowId)], args, GraphQL_FragmentGenerator$ReventlessCore.emptyCapability, "name", undefined, undefined);
|
|
80
|
+
let conn = QueryDbListQuery$ReventlessCore.run([row(rowId)], args, GraphQL_FragmentGenerator$ReventlessCore.emptyCapability, "name", undefined, undefined, undefined);
|
|
81
81
|
return Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(conn), d => d["edges"]), Stdlib_JSON.Decode.array), []).length;
|
|
82
82
|
};
|
|
83
83
|
globalThis.test("storage key row, storage key filter", () => {
|
|
@@ -31,10 +31,11 @@ let emptySpec: Reventless.StateAnnotations.stateAnnotationSpec = {
|
|
|
31
31
|
scanSort: [],
|
|
32
32
|
semantic: [],
|
|
33
33
|
metric: [],
|
|
34
|
-
|
|
34
|
+
lifecycle: None,
|
|
35
35
|
groupBy: None,
|
|
36
36
|
visibility: None,
|
|
37
37
|
live: None,
|
|
38
|
+
retired: None,
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
describe("SuryToJsonSchema:", () => {
|
|
@@ -420,6 +421,145 @@ describe("SuryToJsonSchema:", () => {
|
|
|
420
421
|
))->toEqual((Some("sum"), None))
|
|
421
422
|
})
|
|
422
423
|
|
|
424
|
+
testSync("emits x-reventless-retired {label,showWhenFalse} on the retired field", () => {
|
|
425
|
+
let schema = S.schema(s =>
|
|
426
|
+
{
|
|
427
|
+
"id": s.matches(S.string),
|
|
428
|
+
"archived": s.matches(S.bool),
|
|
429
|
+
}
|
|
430
|
+
)->S.castToUnknown
|
|
431
|
+
let schema' =
|
|
432
|
+
schema->withSpec({
|
|
433
|
+
...emptySpec,
|
|
434
|
+
retired: Some({field: "archived", label: "Archived", showWhenFalse: true, values: None}),
|
|
435
|
+
})
|
|
436
|
+
let json = SuryToJsonSchema.deriveObjectSchema(schema')
|
|
437
|
+
let retiredObj =
|
|
438
|
+
getPropertyOf(json, "archived")->Option.flatMap(s => getProperty(s, "x-reventless-retired"))
|
|
439
|
+
expect((
|
|
440
|
+
retiredObj->Option.flatMap(r => getProperty(r, "label"))->Option.flatMap(JSON.Decode.string),
|
|
441
|
+
retiredObj
|
|
442
|
+
->Option.flatMap(r => getProperty(r, "showWhenFalse"))
|
|
443
|
+
->Option.flatMap(JSON.Decode.bool),
|
|
444
|
+
// Only the named field carries it.
|
|
445
|
+
getPropertyOf(json, "id")->Option.flatMap(s => getProperty(s, "x-reventless-retired")),
|
|
446
|
+
))->toEqual((Some("Archived"), Some(true), None))
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
// The state form. `values` is what tells a consumer which of the two forms a
|
|
450
|
+
// view declared, so it travels on the same key and only when present.
|
|
451
|
+
let stateFormJson = (~values: array<string>) => {
|
|
452
|
+
let schema = S.schema(s =>
|
|
453
|
+
{
|
|
454
|
+
"customerId": s.matches(S.string),
|
|
455
|
+
"accountStatus": s.matches(S.union([S.literal("Active"), S.literal("Deactivated")])),
|
|
456
|
+
}
|
|
457
|
+
)->S.castToUnknown
|
|
458
|
+
schema
|
|
459
|
+
->withSpec({
|
|
460
|
+
...emptySpec,
|
|
461
|
+
lifecycle: Some("accountStatus"),
|
|
462
|
+
retired: Some({
|
|
463
|
+
field: "accountStatus",
|
|
464
|
+
label: "",
|
|
465
|
+
showWhenFalse: false,
|
|
466
|
+
values: Some(values),
|
|
467
|
+
}),
|
|
468
|
+
})
|
|
469
|
+
->SuryToJsonSchema.deriveObjectSchema
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let retiredOn = (json, field) =>
|
|
473
|
+
getPropertyOf(json, field)->Option.flatMap(s => getProperty(s, "x-reventless-retired"))
|
|
474
|
+
|
|
475
|
+
let stringsAt = (retiredObj, key) =>
|
|
476
|
+
retiredObj
|
|
477
|
+
->Option.flatMap(r => getProperty(r, key))
|
|
478
|
+
->Option.flatMap(JSON.Decode.array)
|
|
479
|
+
->Option.map(a => a->Array.filterMap(JSON.Decode.string))
|
|
480
|
+
|
|
481
|
+
testSync("carries one retirement state as a one-element set", () => {
|
|
482
|
+
expect(
|
|
483
|
+
stateFormJson(~values=["Deactivated"])->retiredOn("accountStatus")->stringsAt("values"),
|
|
484
|
+
)->toEqual(Some(["Deactivated"]))
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
testSync("carries every retirement state a lifecycle declares", () => {
|
|
488
|
+
expect(
|
|
489
|
+
stateFormJson(~values=["Archived", "Discontinued"])
|
|
490
|
+
->retiredOn("accountStatus")
|
|
491
|
+
->stringsAt("values"),
|
|
492
|
+
)->toEqual(Some(["Archived", "Discontinued"]))
|
|
493
|
+
})
|
|
494
|
+
|
|
495
|
+
// The transitional key, for one release. A consumer pinned before the set
|
|
496
|
+
// degrades to nothing at all rather than to a missing badge — and enforcement
|
|
497
|
+
// never depended on it, so the whole of the lag is cosmetic either way.
|
|
498
|
+
testSync("emits the singular value beside a one-element set", () => {
|
|
499
|
+
expect(
|
|
500
|
+
stateFormJson(~values=["Deactivated"])
|
|
501
|
+
->retiredOn("accountStatus")
|
|
502
|
+
->Option.flatMap(r => getProperty(r, "value"))
|
|
503
|
+
->Option.flatMap(JSON.Decode.string),
|
|
504
|
+
)->toEqual(Some("Deactivated"))
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
// There is no singular reading of two states, so the shim simply stops. A
|
|
508
|
+
// consumer that understands only `value` sees no retirement rather than one
|
|
509
|
+
// of the two picked arbitrarily.
|
|
510
|
+
testSync("omits the singular value for a set of two", () => {
|
|
511
|
+
expect(
|
|
512
|
+
stateFormJson(~values=["Archived", "Discontinued"])
|
|
513
|
+
->retiredOn("accountStatus")
|
|
514
|
+
->Option.flatMap(r => getProperty(r, "value")),
|
|
515
|
+
)->toEqual(None)
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
// Absent rather than null: a consumer reads the key's absence as "the boolean
|
|
519
|
+
// form", the same way it reads an absent label as "derive one from the name".
|
|
520
|
+
testSync("omits both value and values entirely on the boolean form", () => {
|
|
521
|
+
let schema = S.schema(s => {"archived": s.matches(S.bool)})->S.castToUnknown
|
|
522
|
+
let schema' =
|
|
523
|
+
schema->withSpec({
|
|
524
|
+
...emptySpec,
|
|
525
|
+
retired: Some({field: "archived", label: "", showWhenFalse: false, values: None}),
|
|
526
|
+
})
|
|
527
|
+
let json = SuryToJsonSchema.deriveObjectSchema(schema')
|
|
528
|
+
let retiredObj = json->retiredOn("archived")
|
|
529
|
+
expect((
|
|
530
|
+
retiredObj->Option.flatMap(r => getProperty(r, "value")),
|
|
531
|
+
retiredObj->Option.flatMap(r => getProperty(r, "values")),
|
|
532
|
+
))->toEqual((None, None))
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
testSync("omits the retired label when empty but always states showWhenFalse", () => {
|
|
536
|
+
let schema = S.schema(s => {"deactivated": s.matches(S.bool)})->S.castToUnknown
|
|
537
|
+
let schema' =
|
|
538
|
+
schema->withSpec({
|
|
539
|
+
...emptySpec,
|
|
540
|
+
retired: Some({field: "deactivated", label: "", showWhenFalse: false, values: None}),
|
|
541
|
+
})
|
|
542
|
+
let json = SuryToJsonSchema.deriveObjectSchema(schema')
|
|
543
|
+
let retiredObj =
|
|
544
|
+
getPropertyOf(json, "deactivated")->Option.flatMap(s =>
|
|
545
|
+
getProperty(s, "x-reventless-retired")
|
|
546
|
+
)
|
|
547
|
+
expect((
|
|
548
|
+
retiredObj->Option.flatMap(r => getProperty(r, "label")),
|
|
549
|
+
retiredObj
|
|
550
|
+
->Option.flatMap(r => getProperty(r, "showWhenFalse"))
|
|
551
|
+
->Option.flatMap(JSON.Decode.bool),
|
|
552
|
+
))->toEqual((None, Some(false)))
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
testSync("emits nothing for a schema with no retired annotation", () => {
|
|
556
|
+
let schema = S.schema(s => {"archived": s.matches(S.bool)})->S.castToUnknown
|
|
557
|
+
let json = SuryToJsonSchema.deriveObjectSchema(schema->withSpec(emptySpec))
|
|
558
|
+
expect(
|
|
559
|
+
getPropertyOf(json, "archived")->Option.flatMap(s => getProperty(s, "x-reventless-retired")),
|
|
560
|
+
)->toBe(None)
|
|
561
|
+
})
|
|
562
|
+
|
|
423
563
|
testSync("emits x-reventless-group-by on the field named by groupBy", () => {
|
|
424
564
|
let schema = S.schema(s =>
|
|
425
565
|
{
|