@reventlessdev/reventless-core 3.0.0-alpha.267 → 3.0.0-alpha.268
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
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.268 (2026-09-21)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **core:** a creating command's own id derives no reference ([58a921c](https://github.com/ReventlessDev/reventless-core/commit/58a921c84352f6522e7c175387927e32365aa94a))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.267 (2026-09-21)
|
|
7
14
|
|
|
8
15
|
### 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.268",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -29,15 +29,15 @@
|
|
|
29
29
|
"sury": "11.0.0-rc.2",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
31
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.33",
|
|
32
|
-
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.12",
|
|
33
32
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
|
|
33
|
+
"@reventlessdev/rescript-fast-csv": "2.0.0-alpha.12",
|
|
34
34
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
35
35
|
"@reventlessdev/rescript-node": "2.0.0-alpha.12",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.21",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.12",
|
|
38
38
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
39
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.168",
|
|
40
39
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.37",
|
|
40
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.168",
|
|
41
41
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.140"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
@@ -648,6 +648,9 @@ let identityReference = (
|
|
|
648
648
|
// element schema.
|
|
649
649
|
let extractReferences = (
|
|
650
650
|
~identityViews: option<identityViews>=?,
|
|
651
|
+
// The id a creating command mints. Its identity names the row the command
|
|
652
|
+
// brings into existence, not one it refers to, so nothing is derived for it.
|
|
653
|
+
~minted: option<string>=?,
|
|
651
654
|
properties: dict<S.t<unknown>>,
|
|
652
655
|
): array<Reventless.Plugin.fieldReference> =>
|
|
653
656
|
properties
|
|
@@ -660,8 +663,9 @@ let extractReferences = (
|
|
|
660
663
|
// nesting and the other not.
|
|
661
664
|
let declared = Reventless.Reference.collectFieldTargets(fieldName, fieldSchema)
|
|
662
665
|
let targets = switch identityViews {
|
|
663
|
-
| Some(views)
|
|
664
|
-
|
|
666
|
+
| Some(views) if minted != Some(fieldName) =>
|
|
667
|
+
identityReference(views, ~fieldName, fieldSchema, declared)
|
|
668
|
+
| _ => declared
|
|
665
669
|
}
|
|
666
670
|
targets->Array.map(((path, target)): Reventless.Plugin.fieldReference => {
|
|
667
671
|
Reventless.Plugin.fieldName: path,
|
|
@@ -849,7 +853,6 @@ let toCommandDef = (
|
|
|
849
853
|
~variantName,
|
|
850
854
|
properties,
|
|
851
855
|
)
|
|
852
|
-
let references = extractReferences(~identityViews?, properties)
|
|
853
856
|
// Evaluated against a synthetic value per constructor, the same shape the
|
|
854
857
|
// resolver builds at call time: a payload-bearing variant compiles to
|
|
855
858
|
// `{TAG, ...}`, a payload-less one to a bare string.
|
|
@@ -913,6 +916,11 @@ let toCommandDef = (
|
|
|
913
916
|
| None => Reventless.Transition.targetState(declared)
|
|
914
917
|
}
|
|
915
918
|
let level = derived->Option.flatMap(d => d.level)->Option.getOr(guessedLevel)
|
|
919
|
+
let references = extractReferences(
|
|
920
|
+
~identityViews?,
|
|
921
|
+
~minted=?level == Reventless.Plugin.Collection ? aggregateIdField : None,
|
|
922
|
+
properties,
|
|
923
|
+
)
|
|
916
924
|
// API-exposed iff the whole command isn't @noApi and this variant
|
|
917
925
|
// isn't in its @noApi-variants set — mirrors the API-generation filter
|
|
918
926
|
// (Plugin_Helpers / PluginBaseFragment). Drives the event-graph API badge.
|
|
@@ -481,12 +481,12 @@ function identityReference(identityViews, fieldName, fieldSchema, declared) {
|
|
|
481
481
|
]];
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
-
function extractReferences(identityViews, properties) {
|
|
484
|
+
function extractReferences(identityViews, minted, properties) {
|
|
485
485
|
return Object.entries(properties).flatMap(param => {
|
|
486
486
|
let fieldSchema = param[1];
|
|
487
487
|
let fieldName = param[0];
|
|
488
488
|
let declared = Reference$Reventless.collectFieldTargets(fieldName, fieldSchema);
|
|
489
|
-
let targets = identityViews !== undefined ? identityReference(identityViews, fieldName, fieldSchema, declared) : declared;
|
|
489
|
+
let targets = identityViews !== undefined && Primitive_object.notequal(minted, fieldName) ? identityReference(identityViews, fieldName, fieldSchema, declared) : declared;
|
|
490
490
|
return targets.map(param => {
|
|
491
491
|
let target = param[1];
|
|
492
492
|
return {
|
|
@@ -500,7 +500,7 @@ function extractReferences(identityViews, properties) {
|
|
|
500
500
|
|
|
501
501
|
function toEventDef(identityViews, v) {
|
|
502
502
|
let mkDef = (variantName, properties) => {
|
|
503
|
-
let references = extractReferences(identityViews, properties);
|
|
503
|
+
let references = extractReferences(identityViews, undefined, properties);
|
|
504
504
|
return {
|
|
505
505
|
name: variantName,
|
|
506
506
|
schema: JSON.stringify(SuryToJsonSchema$ReventlessCore.deriveObjectSchema(undefined, undefined, v)),
|
|
@@ -643,7 +643,7 @@ function annotateArgTypes(schema, argTypes) {
|
|
|
643
643
|
function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor, parentSchema, commandAuthorization, commandTransition, derivedEdgeFor, v) {
|
|
644
644
|
let mkDef = (variantName, properties) => {
|
|
645
645
|
let match = commandLevelAndId(isAggregate, partitionKey, variantName, properties);
|
|
646
|
-
let
|
|
646
|
+
let aggregateIdField = match[1];
|
|
647
647
|
let syntheticCommand = DcbTag$Reventless.isVariantPayloadBearing(parentSchema, variantName) ? ({
|
|
648
648
|
TAG: variantName
|
|
649
649
|
}) : variantName;
|
|
@@ -678,6 +678,7 @@ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor
|
|
|
678
678
|
});
|
|
679
679
|
let targetState = observed !== undefined ? observed : Transition$Reventless.targetState(declared);
|
|
680
680
|
let level = Stdlib_Option.getOr(Stdlib_Option.flatMap(derived, d => d.level), match[0]);
|
|
681
|
+
let references = extractReferences(identityViews, level === "Collection" ? aggregateIdField : undefined, properties);
|
|
681
682
|
let apiExposed = false;
|
|
682
683
|
if (!ApiNoApiHelpers$ReventlessCore.isNoApi(parentSchema)) {
|
|
683
684
|
let excluded = ApiNoApiHelpers$ReventlessCore.getExcludedVariants(parentSchema);
|
|
@@ -691,7 +692,7 @@ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor
|
|
|
691
692
|
name: variantName,
|
|
692
693
|
schema: JSON.stringify(annotatedSchema),
|
|
693
694
|
level: level,
|
|
694
|
-
aggregateIdField:
|
|
695
|
+
aggregateIdField: aggregateIdField,
|
|
695
696
|
mutationField: mutationField,
|
|
696
697
|
references: references,
|
|
697
698
|
allowedStates: match$3[0],
|
|
@@ -11,7 +11,7 @@ module CustomerId = Reventless.Id.Make({
|
|
|
11
11
|
let key = "customerId"
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
-
let refsOf = (~viewsByKey, schema: S.t<'a>) => {
|
|
14
|
+
let refsOf = (~viewsByKey, ~minted=?, schema: S.t<'a>) => {
|
|
15
15
|
let reports = []
|
|
16
16
|
let identityViews: Plugin_Structure.identityViews = {
|
|
17
17
|
viewsByKey: Dict.fromArray(viewsByKey),
|
|
@@ -23,7 +23,7 @@ let refsOf = (~viewsByKey, schema: S.t<'a>) => {
|
|
|
23
23
|
| _ => Dict.make()
|
|
24
24
|
}
|
|
25
25
|
let refs =
|
|
26
|
-
Plugin_Structure.extractReferences(~identityViews, properties)->Array.map(r => (
|
|
26
|
+
Plugin_Structure.extractReferences(~identityViews, ~minted?, properties)->Array.map(r => (
|
|
27
27
|
r.fieldName,
|
|
28
28
|
r.entity,
|
|
29
29
|
))
|
|
@@ -63,6 +63,17 @@ describe("a reference derived from the type", () => {
|
|
|
63
63
|
expect(reports->Array.some(r => r->String.includes("no view in this plugin")))->toBe(true)
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
+
// A create form mints this id; a list of the rows that already exist is the
|
|
67
|
+
// wrong control for it.
|
|
68
|
+
testSync("the id a creating command mints references nothing", () => {
|
|
69
|
+
let (refs, reports) = refsOf(
|
|
70
|
+
~viewsByKey=[("customerId", ["Customers"]), ("productId", ["AvailableProducts"])],
|
|
71
|
+
~minted="buyer",
|
|
72
|
+
order,
|
|
73
|
+
)
|
|
74
|
+
expect((refs, reports))->toEqual(([("productIds", "AvailableProducts")], []))
|
|
75
|
+
})
|
|
76
|
+
|
|
66
77
|
testSync("an untyped field is left to its name, as before", () => {
|
|
67
78
|
let (refs, reports) = refsOf(
|
|
68
79
|
~viewsByKey=[("customerId", ["Customers"])],
|
|
@@ -96,4 +107,12 @@ describe("a declared @ref on a typed id", () => {
|
|
|
96
107
|
)
|
|
97
108
|
expect((refs, reports))->toEqual(([("buyer", "Customers")], []))
|
|
98
109
|
})
|
|
110
|
+
|
|
111
|
+
testSync("stands on a minted id: only the derived one steps aside", () => {
|
|
112
|
+
let chosen = S.schema(
|
|
113
|
+
s => {"buyer": s.matches(CustomerId.schema->Reventless.Reference.mark("Customers"))},
|
|
114
|
+
)
|
|
115
|
+
let (refs, _) = refsOf(~viewsByKey=[("customerId", ["Customers"])], ~minted="buyer", chosen)
|
|
116
|
+
expect(refs)->toEqual([("buyer", "Customers")])
|
|
117
|
+
})
|
|
99
118
|
})
|
|
@@ -13,7 +13,7 @@ let CustomerId = Id$Reventless.Make({
|
|
|
13
13
|
key: "customerId"
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
function refsOf(viewsByKey, schema) {
|
|
16
|
+
function refsOf(viewsByKey, minted, schema) {
|
|
17
17
|
let reports = [];
|
|
18
18
|
let identityViews_viewsByKey = Object.fromEntries(viewsByKey);
|
|
19
19
|
let identityViews_viewNames = viewsByKey.flatMap(param => param[1]);
|
|
@@ -27,7 +27,7 @@ function refsOf(viewsByKey, schema) {
|
|
|
27
27
|
};
|
|
28
28
|
let properties;
|
|
29
29
|
properties = schema.type === "object" ? schema.properties : ({});
|
|
30
|
-
let refs = Plugin_Structure$ReventlessCore.extractReferences(identityViews, properties).map(r => [
|
|
30
|
+
let refs = Plugin_Structure$ReventlessCore.extractReferences(identityViews, minted, properties).map(r => [
|
|
31
31
|
r.fieldName,
|
|
32
32
|
r.entity
|
|
33
33
|
]);
|
|
@@ -54,7 +54,7 @@ globalThis.describe("a reference derived from the type", () => {
|
|
|
54
54
|
"productId",
|
|
55
55
|
["AvailableProducts"]
|
|
56
56
|
]
|
|
57
|
-
], order);
|
|
57
|
+
], undefined, order);
|
|
58
58
|
globalThis.expect(match[0]).toEqual([
|
|
59
59
|
[
|
|
60
60
|
"buyer",
|
|
@@ -80,7 +80,7 @@ globalThis.describe("a reference derived from the type", () => {
|
|
|
80
80
|
"ProductDemand"
|
|
81
81
|
]
|
|
82
82
|
]
|
|
83
|
-
], order);
|
|
83
|
+
], undefined, order);
|
|
84
84
|
globalThis.expect(match[0]).toEqual([[
|
|
85
85
|
"buyer",
|
|
86
86
|
"Customers"
|
|
@@ -91,18 +91,40 @@ globalThis.describe("a reference derived from the type", () => {
|
|
|
91
91
|
let match = refsOf([[
|
|
92
92
|
"productId",
|
|
93
93
|
["AvailableProducts"]
|
|
94
|
-
]], order);
|
|
94
|
+
]], undefined, order);
|
|
95
95
|
globalThis.expect(match[0]).toEqual([[
|
|
96
96
|
"productIds",
|
|
97
97
|
"AvailableProducts"
|
|
98
98
|
]]);
|
|
99
99
|
globalThis.expect(match[1].some(r => r.includes("no view in this plugin"))).toBe(true);
|
|
100
100
|
});
|
|
101
|
+
globalThis.test("the id a creating command mints references nothing", () => {
|
|
102
|
+
let match = refsOf([
|
|
103
|
+
[
|
|
104
|
+
"customerId",
|
|
105
|
+
["Customers"]
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"productId",
|
|
109
|
+
["AvailableProducts"]
|
|
110
|
+
]
|
|
111
|
+
], "buyer", order);
|
|
112
|
+
globalThis.expect([
|
|
113
|
+
match[0],
|
|
114
|
+
match[1]
|
|
115
|
+
]).toEqual([
|
|
116
|
+
[[
|
|
117
|
+
"productIds",
|
|
118
|
+
"AvailableProducts"
|
|
119
|
+
]],
|
|
120
|
+
[]
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
101
123
|
globalThis.test("an untyped field is left to its name, as before", () => {
|
|
102
124
|
let match = refsOf([[
|
|
103
125
|
"customerId",
|
|
104
126
|
["Customers"]
|
|
105
|
-
]], Sury.$schema(s => ({
|
|
127
|
+
]], undefined, Sury.$schema(s => ({
|
|
106
128
|
customerId: s.m(Sury.string)
|
|
107
129
|
})));
|
|
108
130
|
globalThis.expect([
|
|
@@ -129,7 +151,7 @@ globalThis.describe("a declared @ref on a typed id", () => {
|
|
|
129
151
|
"productId",
|
|
130
152
|
["Products"]
|
|
131
153
|
]
|
|
132
|
-
], referenced);
|
|
154
|
+
], undefined, referenced);
|
|
133
155
|
globalThis.expect(match[0]).toEqual([[
|
|
134
156
|
"buyer",
|
|
135
157
|
"Products"
|
|
@@ -146,7 +168,7 @@ globalThis.describe("a declared @ref on a typed id", () => {
|
|
|
146
168
|
"Customers",
|
|
147
169
|
"VipCustomers"
|
|
148
170
|
]
|
|
149
|
-
]], chosen);
|
|
171
|
+
]], undefined, chosen);
|
|
150
172
|
globalThis.expect([
|
|
151
173
|
match[0],
|
|
152
174
|
match[1]
|
|
@@ -158,6 +180,19 @@ globalThis.describe("a declared @ref on a typed id", () => {
|
|
|
158
180
|
[]
|
|
159
181
|
]);
|
|
160
182
|
});
|
|
183
|
+
globalThis.test("stands on a minted id: only the derived one steps aside", () => {
|
|
184
|
+
let chosen = Sury.$schema(s => ({
|
|
185
|
+
buyer: s.m(Reference$Reventless.mark(CustomerId.schema, undefined, undefined, "Customers"))
|
|
186
|
+
}));
|
|
187
|
+
let match = refsOf([[
|
|
188
|
+
"customerId",
|
|
189
|
+
["Customers"]
|
|
190
|
+
]], "buyer", chosen);
|
|
191
|
+
globalThis.expect(match[0]).toEqual([[
|
|
192
|
+
"buyer",
|
|
193
|
+
"Customers"
|
|
194
|
+
]]);
|
|
195
|
+
});
|
|
161
196
|
});
|
|
162
197
|
|
|
163
198
|
export {
|