@reventlessdev/reventless-core 3.0.0-alpha.190 → 3.0.0-alpha.192
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 +22 -0
- package/package.json +5 -5
- package/src/admin/Platform_Admin.res +2 -2
- package/src/admin/Platform_Admin.res.mjs +6 -2
- package/src/admin/Platform_Admin_Structure.res +4 -0
- package/src/admin/Platform_Admin_Structure.res.mjs +5 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res +5 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
- package/src/components/Aggregate/Aggregate.res +7 -7
- package/src/components/Aggregate/Aggregate.res.mjs +7 -2
- package/src/components/Aggregate/Aggregate_Builder.res +4 -1
- package/src/components/Aggregate/Aggregate_Builder.res.mjs +11 -3
- package/src/components/Builder_Helpers.res +37 -4
- package/src/components/Builder_Helpers.res.mjs +24 -2
- package/src/components/Dcb/Dcb_Builder.res +2 -2
- package/src/components/Dcb/Dcb_Builder.res.mjs +4 -4
- package/src/components/SideEffectHandler/SideEffectHandler.res +11 -0
- package/src/components/SideEffectHandler/SideEffectHandler_Builder.res +2 -0
- package/src/components/SideEffectHandler/SideEffectHandler_Builder.res.mjs +3 -1
- package/src/components/Task/Task_Builder.res +11 -0
- package/src/components/Task/Task_Builder.res.mjs +2 -0
- package/src/plugin/component/Capability_Inference.res +68 -0
- package/src/plugin/component/Capability_Inference.res.mjs +79 -0
- package/src/plugin/component/Plugin_Builder.res +2 -2
- package/src/plugin/component/Plugin_Builder.res.mjs +4 -4
- package/src/plugin/component/Plugin_Helpers.res +18 -7
- package/src/plugin/component/Plugin_Helpers.res.mjs +20 -1
- package/src/plugin/component/Plugin_Structure.res +215 -79
- package/src/plugin/component/Plugin_Structure.res.mjs +195 -50
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +5 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +15 -4
- package/tests/message/MessageTest.res +1 -0
- package/tests/message/MessageTest.res.mjs +2 -1
- package/tests/plugin/PluginStructureTest.res +391 -0
- package/tests/plugin/PluginStructureTest.res.mjs +355 -0
- package/tests/plugin/StateChangeSlice/PsChangePhoto.res +33 -0
- package/tests/plugin/StateChangeSlice/PsChangePhoto.res.mjs +64 -0
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
3
4
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
5
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
6
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
8
|
+
import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
|
|
9
|
+
import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
|
|
10
|
+
import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
|
|
6
11
|
import * as PsShipOrder$ReventlessCore from "./StateChangeSlice/PsShipOrder.res.mjs";
|
|
7
12
|
import * as PsOrdersView$ReventlessCore from "./StateViewSlice/PsOrdersView.res.mjs";
|
|
8
13
|
import * as PsPlaceOrder$ReventlessCore from "./StateChangeSlice/PsPlaceOrder.res.mjs";
|
|
14
|
+
import * as PsChangePhoto$ReventlessCore from "./StateChangeSlice/PsChangePhoto.res.mjs";
|
|
15
|
+
import * as CapabilityManifest$Reventless from "@reventlessdev/reventless-spec/src/components/CapabilityManifest.res.mjs";
|
|
9
16
|
import * as PsAnnotatedView$ReventlessCore from "./StateViewSlice/PsAnnotatedView.res.mjs";
|
|
10
17
|
import * as PsAttachInvoice$ReventlessCore from "./StateChangeSlice/PsAttachInvoice.res.mjs";
|
|
11
18
|
import * as PsCustomersView$ReventlessCore from "./StateViewSlice/PsCustomersView.res.mjs";
|
|
12
19
|
import * as Plugin_Structure$ReventlessCore from "../../src/plugin/component/Plugin_Structure.res.mjs";
|
|
20
|
+
import * as Capability_Inference$ReventlessCore from "../../src/plugin/component/Capability_Inference.res.mjs";
|
|
13
21
|
import * as PsAvailableProductsView$ReventlessCore from "./StateViewSlice/PsAvailableProductsView.res.mjs";
|
|
14
22
|
|
|
15
23
|
let Behavior = {
|
|
@@ -206,6 +214,36 @@ let PsAttachInvoiceSlice = {
|
|
|
206
214
|
make: make$6
|
|
207
215
|
};
|
|
208
216
|
|
|
217
|
+
let Behavior$3 = {
|
|
218
|
+
initialState: false,
|
|
219
|
+
evolve: PsChangePhoto$ReventlessCore.evolve,
|
|
220
|
+
decide: PsChangePhoto$ReventlessCore.decide,
|
|
221
|
+
moduleUrl: PsChangePhoto$ReventlessCore.moduleUrl
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
function make$7(param, param$1, param$2, param$3, param$4, param$5) {
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let PsChangePhotoSlice_Spec = {
|
|
229
|
+
name: PsChangePhoto$ReventlessCore.name,
|
|
230
|
+
moduleUrl: PsChangePhoto$ReventlessCore.moduleUrl,
|
|
231
|
+
Id: Id$Reventless.$$String,
|
|
232
|
+
consumedEventSchema: PsChangePhoto$ReventlessCore.consumedEventSchema,
|
|
233
|
+
errorSchema: PsChangePhoto$ReventlessCore.errorSchema,
|
|
234
|
+
eventSchema: PsChangePhoto$ReventlessCore.eventSchema,
|
|
235
|
+
commandSchema: PsChangePhoto$ReventlessCore.commandSchema,
|
|
236
|
+
commandAuthorization: PsChangePhoto$ReventlessCore.commandAuthorization,
|
|
237
|
+
readConsistency: PsChangePhoto$ReventlessCore.readConsistency
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
let PsChangePhotoSlice = {
|
|
241
|
+
Spec: PsChangePhotoSlice_Spec,
|
|
242
|
+
Behavior: Behavior$3,
|
|
243
|
+
isAsync: false,
|
|
244
|
+
make: make$7
|
|
245
|
+
};
|
|
246
|
+
|
|
209
247
|
let structure = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [
|
|
210
248
|
PsOrdersViewSlice,
|
|
211
249
|
PsAvailableProductsViewSlice,
|
|
@@ -395,6 +433,218 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
395
433
|
]);
|
|
396
434
|
});
|
|
397
435
|
});
|
|
436
|
+
globalThis.describe("labelField — the shape rule", () => {
|
|
437
|
+
let labelOf = (entityNameOpt, schema) => {
|
|
438
|
+
let entityName = entityNameOpt !== undefined ? entityNameOpt : "Test";
|
|
439
|
+
let r = Plugin_Structure$ReventlessCore.labelFieldsFromStateSchema(entityName, schema);
|
|
440
|
+
return [
|
|
441
|
+
r.field,
|
|
442
|
+
r.searchableFields
|
|
443
|
+
];
|
|
444
|
+
};
|
|
445
|
+
globalThis.test("a DateTime is not a name — a state whose only string is one falls to id", () => {
|
|
446
|
+
let schema = S.schema(s => ({
|
|
447
|
+
orderId: s.m(Reference$Reventless.to_(undefined, undefined, "Order")),
|
|
448
|
+
placedAt: s.m(DateTime$Reventless.string),
|
|
449
|
+
shippedAt: s.m(DateTime$Reventless.string)
|
|
450
|
+
}));
|
|
451
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
452
|
+
"id",
|
|
453
|
+
[]
|
|
454
|
+
]);
|
|
455
|
+
});
|
|
456
|
+
globalThis.test("a DateTime is skipped, not fatal — a later plain string still wins", () => {
|
|
457
|
+
let schema = S.schema(s => ({
|
|
458
|
+
placedAt: s.m(DateTime$Reventless.string),
|
|
459
|
+
reference: s.m(S.string)
|
|
460
|
+
}));
|
|
461
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
462
|
+
"reference",
|
|
463
|
+
["reference"]
|
|
464
|
+
]);
|
|
465
|
+
});
|
|
466
|
+
globalThis.test("an optional string is still the entity's name", () => {
|
|
467
|
+
let schema = S.schema(s => ({
|
|
468
|
+
nickname: s.m(S.option(S.string))
|
|
469
|
+
}));
|
|
470
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
471
|
+
"nickname",
|
|
472
|
+
["nickname"]
|
|
473
|
+
]);
|
|
474
|
+
});
|
|
475
|
+
globalThis.test("a reference is not a name even when its field is not called `*Id`", () => {
|
|
476
|
+
let schema = S.schema(s => ({
|
|
477
|
+
customer: s.m(Reference$Reventless.to_(undefined, undefined, "Customer")),
|
|
478
|
+
note: s.m(S.string)
|
|
479
|
+
}));
|
|
480
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
481
|
+
"note",
|
|
482
|
+
["note"]
|
|
483
|
+
]);
|
|
484
|
+
});
|
|
485
|
+
globalThis.test("a DCB-tagged string is not a name", () => {
|
|
486
|
+
let schema = S.schema(s => ({
|
|
487
|
+
partition: s.m(DcbTag$Reventless.string),
|
|
488
|
+
note: s.m(S.string)
|
|
489
|
+
}));
|
|
490
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
491
|
+
"note",
|
|
492
|
+
["note"]
|
|
493
|
+
]);
|
|
494
|
+
});
|
|
495
|
+
globalThis.test("the `*Id` test is case-insensitive — `productID` is a reference", () => {
|
|
496
|
+
let schema = S.schema(s => ({
|
|
497
|
+
productID: s.m(S.string),
|
|
498
|
+
note: s.m(S.string)
|
|
499
|
+
}));
|
|
500
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
501
|
+
"note",
|
|
502
|
+
["note"]
|
|
503
|
+
]);
|
|
504
|
+
});
|
|
505
|
+
globalThis.test("a semantic-carrying string is a value, not a name", () => {
|
|
506
|
+
let schema = S.schema(s => ({
|
|
507
|
+
imageUrl: s.m(StorageRef$Reventless.forStore(undefined, "productImages")),
|
|
508
|
+
note: s.m(S.string)
|
|
509
|
+
}));
|
|
510
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
511
|
+
"note",
|
|
512
|
+
["note"]
|
|
513
|
+
]);
|
|
514
|
+
});
|
|
515
|
+
globalThis.test("a state whose only string is a semantic falls to id", () => {
|
|
516
|
+
let schema = S.schema(s => ({
|
|
517
|
+
imageUrl: s.m(StorageRef$Reventless.forStore(undefined, "productImages"))
|
|
518
|
+
}));
|
|
519
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
520
|
+
"id",
|
|
521
|
+
[]
|
|
522
|
+
]);
|
|
523
|
+
});
|
|
524
|
+
globalThis.test("`id` itself is still excluded by name", () => {
|
|
525
|
+
let schema = S.schema(s => ({
|
|
526
|
+
id: s.m(S.string),
|
|
527
|
+
note: s.m(S.string)
|
|
528
|
+
}));
|
|
529
|
+
globalThis.expect(labelOf(undefined, schema)).toEqual([
|
|
530
|
+
"note",
|
|
531
|
+
["note"]
|
|
532
|
+
]);
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
globalThis.describe("labelField — the conventional-name rung", () => {
|
|
536
|
+
let labelOf = schema => {
|
|
537
|
+
let r = Plugin_Structure$ReventlessCore.labelFieldsFromStateSchema("Test", schema);
|
|
538
|
+
return [
|
|
539
|
+
r.field,
|
|
540
|
+
r.searchableFields
|
|
541
|
+
];
|
|
542
|
+
};
|
|
543
|
+
globalThis.test("a field named `name` beats an earlier string", () => {
|
|
544
|
+
let schema = S.schema(s => ({
|
|
545
|
+
sku: s.m(S.string),
|
|
546
|
+
name: s.m(S.string)
|
|
547
|
+
}));
|
|
548
|
+
globalThis.expect(labelOf(schema)).toEqual([
|
|
549
|
+
"name",
|
|
550
|
+
["name"]
|
|
551
|
+
]);
|
|
552
|
+
});
|
|
553
|
+
globalThis.test("`title` counts too, case-insensitively", () => {
|
|
554
|
+
let schema = S.schema(s => ({
|
|
555
|
+
body: s.m(S.string),
|
|
556
|
+
Title: s.m(S.string)
|
|
557
|
+
}));
|
|
558
|
+
globalThis.expect(labelOf(schema)).toEqual([
|
|
559
|
+
"Title",
|
|
560
|
+
["Title"]
|
|
561
|
+
]);
|
|
562
|
+
});
|
|
563
|
+
globalThis.test("the match is exact — `customerName` is a customer's name, not this record's", () => {
|
|
564
|
+
let schema = S.schema(s => ({
|
|
565
|
+
customerName: s.m(S.string),
|
|
566
|
+
note: s.m(S.string)
|
|
567
|
+
}));
|
|
568
|
+
globalThis.expect(labelOf(schema)).toEqual([
|
|
569
|
+
"customerName",
|
|
570
|
+
["customerName"]
|
|
571
|
+
]);
|
|
572
|
+
});
|
|
573
|
+
globalThis.test("an optional `name` still outranks an earlier string", () => {
|
|
574
|
+
let schema = S.schema(s => ({
|
|
575
|
+
sku: s.m(S.string),
|
|
576
|
+
name: s.m(S.option(S.string))
|
|
577
|
+
}));
|
|
578
|
+
globalThis.expect(labelOf(schema)).toEqual([
|
|
579
|
+
"name",
|
|
580
|
+
["name"]
|
|
581
|
+
]);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
globalThis.describe("labelFieldSource — which rung answered", () => {
|
|
585
|
+
let sourceOf = schema => Plugin_Structure$ReventlessCore.labelFieldSourceToString(Plugin_Structure$ReventlessCore.labelFieldsFromStateSchema("Test", schema).source);
|
|
586
|
+
globalThis.test("a @displayName spec is a declaration", () => {
|
|
587
|
+
let customers = structure.stateViewSlices[2];
|
|
588
|
+
globalThis.expect([
|
|
589
|
+
customers.labelField,
|
|
590
|
+
customers.labelFieldSource
|
|
591
|
+
]).toEqual([
|
|
592
|
+
"displayName",
|
|
593
|
+
"annotation"
|
|
594
|
+
]);
|
|
595
|
+
});
|
|
596
|
+
globalThis.test("a field named `name` is a guess the consumer can also make", () => {
|
|
597
|
+
let schema = S.schema(s => ({
|
|
598
|
+
sku: s.m(S.string),
|
|
599
|
+
name: s.m(S.string)
|
|
600
|
+
}));
|
|
601
|
+
globalThis.expect(sourceOf(schema)).toEqual("convention");
|
|
602
|
+
});
|
|
603
|
+
globalThis.test("declaration order is a guess only this side can make", () => {
|
|
604
|
+
let schema = S.schema(s => ({
|
|
605
|
+
customerName: s.m(S.string),
|
|
606
|
+
note: s.m(S.string)
|
|
607
|
+
}));
|
|
608
|
+
globalThis.expect(sourceOf(schema)).toEqual("position");
|
|
609
|
+
});
|
|
610
|
+
globalThis.test("no candidate at all is the state saying it has no human field", () => {
|
|
611
|
+
let schema = S.schema(s => ({
|
|
612
|
+
orderId: s.m(Reference$Reventless.to_(undefined, undefined, "Order")),
|
|
613
|
+
placedAt: s.m(DateTime$Reventless.string)
|
|
614
|
+
}));
|
|
615
|
+
globalThis.expect(sourceOf(schema)).toEqual("fallback");
|
|
616
|
+
});
|
|
617
|
+
globalThis.test("every built def states one", () => {
|
|
618
|
+
let stated = structure.readModels.concat(structure.stateViewSlices).every(q => Stdlib_Option.isSome(q.labelFieldSource));
|
|
619
|
+
globalThis.expect(stated).toEqual(true);
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
globalThis.describe("statusField — the shape rule", () => {
|
|
623
|
+
globalThis.test("a `status` field holding a closed set of values is the lifecycle field", () => {
|
|
624
|
+
let schema = S.schema(s => ({
|
|
625
|
+
status: s.m(S.union([
|
|
626
|
+
S.literal("Placed"),
|
|
627
|
+
S.literal("Shipped")
|
|
628
|
+
]))
|
|
629
|
+
}));
|
|
630
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.statusFieldFromStateSchema("Test", schema)).toEqual("status");
|
|
631
|
+
});
|
|
632
|
+
globalThis.test("an optional one counts too", () => {
|
|
633
|
+
let schema = S.schema(s => ({
|
|
634
|
+
status: s.m(S.option(S.union([
|
|
635
|
+
S.literal("Placed"),
|
|
636
|
+
S.literal("Shipped")
|
|
637
|
+
])))
|
|
638
|
+
}));
|
|
639
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.statusFieldFromStateSchema("Test", schema)).toEqual("status");
|
|
640
|
+
});
|
|
641
|
+
globalThis.test("free text named `status` is not a lifecycle", () => {
|
|
642
|
+
let schema = S.schema(s => ({
|
|
643
|
+
status: s.m(S.string)
|
|
644
|
+
}));
|
|
645
|
+
globalThis.expect(Plugin_Structure$ReventlessCore.statusFieldFromStateSchema("Test", schema)).toEqual(undefined);
|
|
646
|
+
});
|
|
647
|
+
});
|
|
398
648
|
globalThis.describe("queryableDef.schema propagates x-reventless-* annotations", () => {
|
|
399
649
|
let getProperty = (json, key) => {
|
|
400
650
|
let obj = Stdlib_JSON.Decode.object(json);
|
|
@@ -502,6 +752,110 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
|
|
|
502
752
|
globalThis.test("a plugin declaring no stores collects none — not None", () => {
|
|
503
753
|
globalThis.expect(structure.requiredStores).toEqual([]);
|
|
504
754
|
});
|
|
755
|
+
globalThis.test("each requirement keeps its declaring (component, field) sites", () => {
|
|
756
|
+
let expected = [
|
|
757
|
+
{
|
|
758
|
+
store: "TestPlugin.documents",
|
|
759
|
+
component: "AttachInvoice",
|
|
760
|
+
field: "documentUrl"
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
store: "branding.logos",
|
|
764
|
+
component: "AttachInvoice",
|
|
765
|
+
field: "logoUrl"
|
|
766
|
+
}
|
|
767
|
+
];
|
|
768
|
+
globalThis.expect(withStores.requiredStoreDeclarations).toEqual(expected);
|
|
769
|
+
});
|
|
770
|
+
globalThis.describe("capability manifest", () => {
|
|
771
|
+
let manifest = CapabilityManifest$Reventless.fromStructure(withStores);
|
|
772
|
+
globalThis.test("keys are byte-identical to requiredStores", () => {
|
|
773
|
+
globalThis.expect(manifest.capabilities.map(c => c.key)).toEqual(Stdlib_Option.getOr(withStores.requiredStores, []));
|
|
774
|
+
});
|
|
775
|
+
globalThis.test("each entry carries its provenance", () => {
|
|
776
|
+
let entry = Stdlib_Option.getOrThrow(manifest.capabilities.find(c => c.key === "TestPlugin.documents"), undefined);
|
|
777
|
+
let expected = [{
|
|
778
|
+
component: "AttachInvoice",
|
|
779
|
+
field: "documentUrl"
|
|
780
|
+
}];
|
|
781
|
+
globalThis.expect(entry.declaredBy).toEqual(expected);
|
|
782
|
+
});
|
|
783
|
+
globalThis.test("a structure declaring nothing yields an empty list, not a missing manifest", () => {
|
|
784
|
+
globalThis.expect(CapabilityManifest$Reventless.fromStructure(structure).capabilities).toEqual([]);
|
|
785
|
+
});
|
|
786
|
+
globalThis.test("rendering is deterministic and newline-terminated", () => {
|
|
787
|
+
let rendered = CapabilityManifest$Reventless.renderForStructure(withStores);
|
|
788
|
+
globalThis.expect(rendered).toBe(`{
|
|
789
|
+
"capabilities": [
|
|
790
|
+
{
|
|
791
|
+
"kind": "ObjectStore",
|
|
792
|
+
"key": "TestPlugin.documents",
|
|
793
|
+
"declaredBy": [
|
|
794
|
+
{
|
|
795
|
+
"component": "AttachInvoice",
|
|
796
|
+
"field": "documentUrl"
|
|
797
|
+
}
|
|
798
|
+
]
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
"kind": "ObjectStore",
|
|
802
|
+
"key": "branding.logos",
|
|
803
|
+
"declaredBy": [
|
|
804
|
+
{
|
|
805
|
+
"component": "AttachInvoice",
|
|
806
|
+
"field": "logoUrl"
|
|
807
|
+
}
|
|
808
|
+
]
|
|
809
|
+
}
|
|
810
|
+
]
|
|
811
|
+
}
|
|
812
|
+
`);
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
});
|
|
816
|
+
globalThis.describe("capability inference", () => {
|
|
817
|
+
let commandWarnings = Capability_Inference$ReventlessCore.scanSchema("ChangePhoto", PsChangePhoto$ReventlessCore.commandSchema);
|
|
818
|
+
let eventWarnings = Capability_Inference$ReventlessCore.scanSchema("ChangePhoto", PsChangePhoto$ReventlessCore.eventSchema);
|
|
819
|
+
globalThis.test("a heuristic-only match produces a warning", () => {
|
|
820
|
+
let expected = [{
|
|
821
|
+
component: "ChangePhoto",
|
|
822
|
+
field: "photoUrl"
|
|
823
|
+
}];
|
|
824
|
+
globalThis.expect(commandWarnings).toEqual(expected);
|
|
825
|
+
});
|
|
826
|
+
globalThis.test("a declared field with a heuristic name does not warn", () => {
|
|
827
|
+
let expected = [{
|
|
828
|
+
component: "ChangePhoto",
|
|
829
|
+
field: "photoUrl"
|
|
830
|
+
}];
|
|
831
|
+
globalThis.expect(eventWarnings).toEqual(expected);
|
|
832
|
+
});
|
|
833
|
+
globalThis.test("a heuristic-only match reaches no manifest entry", () => {
|
|
834
|
+
let withHeuristicField = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsChangePhotoSlice], undefined, undefined, undefined, undefined, undefined, undefined);
|
|
835
|
+
globalThis.expect(withHeuristicField.requiredStores).toEqual(["TestPlugin.productPhotos"]);
|
|
836
|
+
let manifest = CapabilityManifest$Reventless.fromStructure(withHeuristicField);
|
|
837
|
+
globalThis.expect(manifest.capabilities.every(entry => entry.declaredBy.every(site => site.field !== "photoUrl"))).toBe(true);
|
|
838
|
+
});
|
|
839
|
+
globalThis.test("the warning names the field and the settling annotation", () => {
|
|
840
|
+
let text = Capability_Inference$ReventlessCore.message(commandWarnings[0]);
|
|
841
|
+
globalThis.expect(text.includes("ChangePhoto.photoUrl")).toBe(true);
|
|
842
|
+
globalThis.expect(text.includes("@storageRef")).toBe(true);
|
|
843
|
+
});
|
|
844
|
+
globalThis.test("suffix and exact name rules match; ordinary names do not", () => {
|
|
845
|
+
globalThis.expect([
|
|
846
|
+
"invoiceFileRef",
|
|
847
|
+
"upload",
|
|
848
|
+
"thumbnail",
|
|
849
|
+
"customerName",
|
|
850
|
+
"documentUrl"
|
|
851
|
+
].map(Capability_Inference$ReventlessCore.nameMatches)).toEqual([
|
|
852
|
+
true,
|
|
853
|
+
true,
|
|
854
|
+
true,
|
|
855
|
+
false,
|
|
856
|
+
false
|
|
857
|
+
]);
|
|
858
|
+
});
|
|
505
859
|
});
|
|
506
860
|
});
|
|
507
861
|
|
|
@@ -513,6 +867,7 @@ export {
|
|
|
513
867
|
PsCustomersViewSlice,
|
|
514
868
|
PsAnnotatedViewSlice,
|
|
515
869
|
PsAttachInvoiceSlice,
|
|
870
|
+
PsChangePhotoSlice,
|
|
516
871
|
structure,
|
|
517
872
|
}
|
|
518
873
|
/* structure Not a pure module */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Test fixture spec for heuristic capability inference.
|
|
2
|
+
//
|
|
3
|
+
// `photoUrl` matches the stored-object name heuristic but carries no
|
|
4
|
+
// `@storageRef` — the case that must produce a warning and no manifest entry.
|
|
5
|
+
// `caption` matches nothing, so the scan's silence on it is also exercised.
|
|
6
|
+
|
|
7
|
+
@@reventless.spec("ChangePhoto")
|
|
8
|
+
|
|
9
|
+
type state = bool
|
|
10
|
+
let initialState = false
|
|
11
|
+
|
|
12
|
+
@schema
|
|
13
|
+
type consumedEvent = OrderPlaced
|
|
14
|
+
|
|
15
|
+
let evolve = (_state, _event) => true
|
|
16
|
+
|
|
17
|
+
@schema
|
|
18
|
+
type command = ChangePhoto({productId: string, photoUrl: string, caption: string})
|
|
19
|
+
|
|
20
|
+
@schema
|
|
21
|
+
type error = ProductNotFound
|
|
22
|
+
|
|
23
|
+
// `thumbnail` matches the same name heuristic but IS declared — the case the
|
|
24
|
+
// lint must stay silent on, because the declaration already provisions it.
|
|
25
|
+
@schema
|
|
26
|
+
type event =
|
|
27
|
+
| PhotoChanged({
|
|
28
|
+
productId: string,
|
|
29
|
+
photoUrl: string,
|
|
30
|
+
@storageRef("productPhotos") thumbnail: string,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
let decide = (_state, _command): result<array<event>, error> => Ok([])
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
|
|
7
|
+
let consumedEventSchema = S.literal("OrderPlaced");
|
|
8
|
+
|
|
9
|
+
function evolve(_state, _event) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let commandSchema = S.schema(s => ({
|
|
14
|
+
TAG: "ChangePhoto",
|
|
15
|
+
productId: s.m(DcbTag$Reventless.string),
|
|
16
|
+
photoUrl: s.m(S.string),
|
|
17
|
+
caption: s.m(S.string)
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
let errorSchema = S.literal("ProductNotFound");
|
|
21
|
+
|
|
22
|
+
let eventSchema = S.schema(s => ({
|
|
23
|
+
TAG: "PhotoChanged",
|
|
24
|
+
productId: s.m(DcbTag$Reventless.string),
|
|
25
|
+
photoUrl: s.m(S.string),
|
|
26
|
+
thumbnail: s.m(StorageRef$Reventless.forStore(undefined, "productPhotos"))
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
function decide(_state, _command) {
|
|
30
|
+
return {
|
|
31
|
+
TAG: "Ok",
|
|
32
|
+
_0: []
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function commandAuthorization(param) {
|
|
37
|
+
return "AllowAuthenticated";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let name = "ChangePhoto";
|
|
41
|
+
|
|
42
|
+
let Id;
|
|
43
|
+
|
|
44
|
+
let initialState = false;
|
|
45
|
+
|
|
46
|
+
let moduleUrl = "@reventlessdev/reventless-core/tests/plugin/StateChangeSlice/PsChangePhoto.res.mjs";
|
|
47
|
+
|
|
48
|
+
let readConsistency = "EscalateOnRetry";
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
name,
|
|
52
|
+
Id,
|
|
53
|
+
initialState,
|
|
54
|
+
consumedEventSchema,
|
|
55
|
+
evolve,
|
|
56
|
+
commandSchema,
|
|
57
|
+
errorSchema,
|
|
58
|
+
eventSchema,
|
|
59
|
+
decide,
|
|
60
|
+
moduleUrl,
|
|
61
|
+
commandAuthorization,
|
|
62
|
+
readConsistency,
|
|
63
|
+
}
|
|
64
|
+
/* consumedEventSchema Not a pure module */
|