@reventlessdev/reventless-core 3.0.0-alpha.184 → 3.0.0-alpha.185
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 +7 -0
- package/package.json +6 -6
- package/src/components/Api/GraphQL_FragmentGenerator.res +13 -2
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +6 -2
- package/src/components/Dcb/Dcb_Builder.res +5 -0
- package/src/components/Dcb/Dcb_Builder.res.mjs +3 -1
- package/tests/api/GraphQL_FragmentGeneratorTest.res +49 -0
- package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +57 -0
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.185 (2026-07-27)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **api:** don't inject id: ID! into multi-variant DCB slice mutations ([03cfaad](https://github.com/ReventlessDev/reventless-core/commit/03cfaadce3bee3d7010835edc10f57ab45ddfa5b))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.184 (2026-07-27)
|
|
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.185",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"sury": "11.0.0-alpha.4",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
31
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.31",
|
|
32
|
+
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
|
|
33
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
32
34
|
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
|
|
33
35
|
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.13",
|
|
34
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
35
|
-
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
|
|
38
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.106",
|
|
38
39
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
|
|
39
|
-
"@reventlessdev/reventless-
|
|
40
|
-
"@reventlessdev/reventless-interop": "3.0.0-alpha.29"
|
|
41
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.81"
|
|
40
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.81",
|
|
41
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.29"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "12.3.0",
|
|
@@ -396,6 +396,12 @@ let generate = (
|
|
|
396
396
|
|
|
397
397
|
mutationEntries->Array.forEach(entry => {
|
|
398
398
|
let schema = entry.commandSchema
|
|
399
|
+
// Aggregates carry the instance id as a separate `id: ID!` argument ahead of
|
|
400
|
+
// the command payload; DCB slices don't (their key field is part of the
|
|
401
|
+
// payload). A multi-variant slice command is a sury `Union` just like an
|
|
402
|
+
// aggregate command, so the injection must key off the entry flag, not the
|
|
403
|
+
// schema shape — see `mutationSchemaEntry.injectIdArg`.
|
|
404
|
+
let injectIdArg = entry.injectIdArg->Option.getOr(true)
|
|
399
405
|
switch schema {
|
|
400
406
|
| Union({anyOf}) =>
|
|
401
407
|
// Map each fieldName to its variant in anyOf by matching the constructor
|
|
@@ -433,7 +439,9 @@ let generate = (
|
|
|
433
439
|
variantSchema,
|
|
434
440
|
) {
|
|
435
441
|
| Some(field) =>
|
|
436
|
-
let withId = if
|
|
442
|
+
let withId = if !injectIdArg {
|
|
443
|
+
field
|
|
444
|
+
} else if field->String.includes("(") {
|
|
437
445
|
field->String.replace(`${fieldName}(`, `${fieldName}(id: ID!, `)
|
|
438
446
|
} else {
|
|
439
447
|
field->String.replace(`${fieldName}:`, `${fieldName}(id: ID!):`)
|
|
@@ -442,7 +450,10 @@ let generate = (
|
|
|
442
450
|
| None =>
|
|
443
451
|
// Payload-less variant (S.literal("Ctor")) — no Object fields to
|
|
444
452
|
// derive args from, but still a valid mutation.
|
|
445
|
-
|
|
453
|
+
let field = injectIdArg
|
|
454
|
+
? ` ${fieldName}(id: ID!): CommandResult!`
|
|
455
|
+
: ` ${fieldName}: CommandResult!`
|
|
456
|
+
mutations->Array.push(field)
|
|
446
457
|
}
|
|
447
458
|
})
|
|
448
459
|
| Object(_) =>
|
|
@@ -319,6 +319,7 @@ function generate(mutationEntries, queryEntries) {
|
|
|
319
319
|
let constructorNameOf = fieldName => Stdlib_Option.getOr(fieldName.split("_")[fieldName.split("_").length - 1 | 0], fieldName);
|
|
320
320
|
mutationEntries.forEach(entry => {
|
|
321
321
|
let schema = entry.commandSchema;
|
|
322
|
+
let injectIdArg = Stdlib_Option.getOr(entry.injectIdArg, true);
|
|
322
323
|
switch (schema.type) {
|
|
323
324
|
case "object" :
|
|
324
325
|
let fieldName = Stdlib_Option.getOr(entry.fieldNames[0], "");
|
|
@@ -361,11 +362,14 @@ function generate(mutationEntries, queryEntries) {
|
|
|
361
362
|
let variantSchema = variantIndex >= 0 ? Stdlib_Option.getOr(anyOf[variantIndex], schema) : schema;
|
|
362
363
|
let field = deriveMutationFieldFromObject(fieldName, types, seenTypes, variantSchema);
|
|
363
364
|
if (field !== undefined) {
|
|
364
|
-
let withId =
|
|
365
|
+
let withId = injectIdArg ? (
|
|
366
|
+
field.includes("(") ? field.replace(fieldName + `(`, fieldName + `(id: ID!, `) : field.replace(fieldName + `:`, fieldName + `(id: ID!):`)
|
|
367
|
+
) : field;
|
|
365
368
|
mutations.push(withId);
|
|
366
369
|
return;
|
|
367
370
|
}
|
|
368
|
-
|
|
371
|
+
let field$1 = injectIdArg ? ` ` + fieldName + `(id: ID!): CommandResult!` : ` ` + fieldName + `: CommandResult!`;
|
|
372
|
+
mutations.push(field$1);
|
|
369
373
|
});
|
|
370
374
|
return;
|
|
371
375
|
default:
|
|
@@ -914,6 +914,10 @@ module Make = (
|
|
|
914
914
|
ReventlessInfra.Api.fieldNames: fieldSpecs->Array.map(((f, _)) => f),
|
|
915
915
|
commandSchema,
|
|
916
916
|
fieldPermissions,
|
|
917
|
+
// Slice key fields (e.g. `orderId`) are payload args; there is no
|
|
918
|
+
// separate aggregate `id`. A multi-variant slice command is a sury
|
|
919
|
+
// `Union`, so without this the generator would inject `id: ID!`.
|
|
920
|
+
injectIdArg: false,
|
|
917
921
|
systemCallable: systemCallableComponents->Array.includes(S.Spec.name),
|
|
918
922
|
linkedViews: ?sliceDef->Option.map(d => d.linkedViews),
|
|
919
923
|
consistencyRead: ?sliceDef->Option.flatMap(d => d.consistencyRead),
|
|
@@ -938,6 +942,7 @@ module Make = (
|
|
|
938
942
|
ReventlessInfra.Api.fieldNames: [fieldName],
|
|
939
943
|
commandSchema: ITS.Spec.externalInputSchema->S.castToUnknown,
|
|
940
944
|
fieldPermissions,
|
|
945
|
+
injectIdArg: false,
|
|
941
946
|
}
|
|
942
947
|
})
|
|
943
948
|
|
|
@@ -484,6 +484,7 @@ function Make(DcbEventLogStorage) {
|
|
|
484
484
|
commandSchema: commandSchema,
|
|
485
485
|
fieldPermissions: fieldPermissions,
|
|
486
486
|
systemCallable: systemCallableComponents.includes(S.Spec.name),
|
|
487
|
+
injectIdArg: false,
|
|
487
488
|
linkedViews: Stdlib_Option.map(sliceDef, d => d.linkedViews),
|
|
488
489
|
consistencyRead: Stdlib_Option.flatMap(sliceDef, d => d.consistencyRead)
|
|
489
490
|
};
|
|
@@ -498,7 +499,8 @@ function Make(DcbEventLogStorage) {
|
|
|
498
499
|
return {
|
|
499
500
|
fieldNames: [fieldName],
|
|
500
501
|
commandSchema: ITS.Spec.externalInputSchema,
|
|
501
|
-
fieldPermissions: fieldPermissions
|
|
502
|
+
fieldPermissions: fieldPermissions,
|
|
503
|
+
injectIdArg: false
|
|
502
504
|
};
|
|
503
505
|
});
|
|
504
506
|
let stateViewEntries = stateViewSlices.map(V => {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// A multi-variant command union serializes as a sury `Union`, the same shape an
|
|
4
|
+
// aggregate command produces. The generator must decide whether to inject the
|
|
5
|
+
// aggregate-style `id: ID!` argument from the entry's `injectIdArg` flag, not
|
|
6
|
+
// from the schema shape — otherwise a multi-variant DCB slice (e.g. CancelOrder
|
|
7
|
+
// + @noApi ReopenOrder) wrongly inherits a spurious `id: ID!` and callers that
|
|
8
|
+
// send only the slice's own key field get `MissingFieldArgument: id`.
|
|
9
|
+
module OrderSliceCmd = {
|
|
10
|
+
@schema
|
|
11
|
+
type command =
|
|
12
|
+
| CancelOrder({orderId: string})
|
|
13
|
+
| ReopenOrder({orderId: string})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let mutationFor = (fragment, needle) =>
|
|
17
|
+
GraphQL_Stitcher.decode(fragment).mutations->Array.find(m => m->String.includes(needle))
|
|
18
|
+
|
|
19
|
+
describe("GraphQL_FragmentGenerator.generate mutation id injection", () => {
|
|
20
|
+
testSync("multi-variant slice entry (injectIdArg: false) omits the id argument", () => {
|
|
21
|
+
let fragment = GraphQL_FragmentGenerator.generate(
|
|
22
|
+
~mutationEntries=[
|
|
23
|
+
{
|
|
24
|
+
ReventlessInfra.Api.fieldNames: ["Ordering_CancelOrder"],
|
|
25
|
+
commandSchema: OrderSliceCmd.commandSchema->S.castToUnknown,
|
|
26
|
+
injectIdArg: false,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
~queryEntries=[],
|
|
30
|
+
)
|
|
31
|
+
let field = mutationFor(fragment, "Ordering_CancelOrder")->Option.getOr("")
|
|
32
|
+
expect(field->String.includes("orderId"))->toBe(true)
|
|
33
|
+
expect(field->String.includes("id: ID!"))->toBe(false)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
testSync("aggregate-style entry (default) injects id: ID! ahead of the payload", () => {
|
|
37
|
+
let fragment = GraphQL_FragmentGenerator.generate(
|
|
38
|
+
~mutationEntries=[
|
|
39
|
+
{
|
|
40
|
+
ReventlessInfra.Api.fieldNames: ["Ordering_CancelOrder"],
|
|
41
|
+
commandSchema: OrderSliceCmd.commandSchema->S.castToUnknown,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
~queryEntries=[],
|
|
45
|
+
)
|
|
46
|
+
let field = mutationFor(fragment, "Ordering_CancelOrder")->Option.getOr("")
|
|
47
|
+
expect(field->String.includes("id: ID!, orderId"))->toBe(true)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as GraphQL_Stitcher$ReventlessCore from "../../src/components/Api/GraphQL_Stitcher.res.mjs";
|
|
6
|
+
import * as GraphQL_FragmentGenerator$ReventlessCore from "../../src/components/Api/GraphQL_FragmentGenerator.res.mjs";
|
|
7
|
+
|
|
8
|
+
let commandSchema = S.union([
|
|
9
|
+
S.schema(s => ({
|
|
10
|
+
TAG: "CancelOrder",
|
|
11
|
+
orderId: s.m(S.string)
|
|
12
|
+
})),
|
|
13
|
+
S.schema(s => ({
|
|
14
|
+
TAG: "ReopenOrder",
|
|
15
|
+
orderId: s.m(S.string)
|
|
16
|
+
}))
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
function commandAuthorization(param) {
|
|
20
|
+
return "AllowAuthenticated";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let OrderSliceCmd = {
|
|
24
|
+
commandSchema: commandSchema,
|
|
25
|
+
commandAuthorization: commandAuthorization
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function mutationFor(fragment, needle) {
|
|
29
|
+
return GraphQL_Stitcher$ReventlessCore.decode(fragment).mutations.find(m => m.includes(needle));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
globalThis.describe("GraphQL_FragmentGenerator.generate mutation id injection", () => {
|
|
33
|
+
globalThis.test("multi-variant slice entry (injectIdArg: false) omits the id argument", () => {
|
|
34
|
+
let fragment = GraphQL_FragmentGenerator$ReventlessCore.generate([{
|
|
35
|
+
fieldNames: ["Ordering_CancelOrder"],
|
|
36
|
+
commandSchema: commandSchema,
|
|
37
|
+
injectIdArg: false
|
|
38
|
+
}], []);
|
|
39
|
+
let field = Stdlib_Option.getOr(mutationFor(fragment, "Ordering_CancelOrder"), "");
|
|
40
|
+
globalThis.expect(field.includes("orderId")).toBe(true);
|
|
41
|
+
globalThis.expect(field.includes("id: ID!")).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
globalThis.test("aggregate-style entry (default) injects id: ID! ahead of the payload", () => {
|
|
44
|
+
let fragment = GraphQL_FragmentGenerator$ReventlessCore.generate([{
|
|
45
|
+
fieldNames: ["Ordering_CancelOrder"],
|
|
46
|
+
commandSchema: commandSchema
|
|
47
|
+
}], []);
|
|
48
|
+
let field = Stdlib_Option.getOr(mutationFor(fragment, "Ordering_CancelOrder"), "");
|
|
49
|
+
globalThis.expect(field.includes("id: ID!, orderId")).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
OrderSliceCmd,
|
|
55
|
+
mutationFor,
|
|
56
|
+
}
|
|
57
|
+
/* commandSchema Not a pure module */
|