@reventlessdev/reventless-core 3.0.0-alpha.243 → 3.0.0-alpha.244
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.244 (2026-08-20)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **api:** name a union after itself, not after the keyword ([58ab956](https://github.com/ReventlessDev/reventless-core/commit/58ab956b8fa310c60f118595768e40e750d61784))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.243 (2026-08-20)
|
|
7
14
|
|
|
8
15
|
### Features
|
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.244",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"@reventlessdev/rescript-node": "2.0.0-alpha.8",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.8",
|
|
38
|
+
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
38
39
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.148",
|
|
39
40
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.120",
|
|
40
|
-
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
41
41
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.32"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
@@ -122,17 +122,19 @@ let collectSubscriptionSources = (
|
|
|
122
122
|
// Extract the first identifier from a SDL field/type string.
|
|
123
123
|
// e.g. " fieldName(args): Type" → "fieldName"
|
|
124
124
|
// "type TypeName {" → "TypeName"
|
|
125
|
+
// Every SDL keyword that introduces a named definition. `union` was missing while
|
|
126
|
+
// `CommandResult` was the only union in the schema: a keyword left in place makes
|
|
127
|
+
// every declaration of that kind name itself after the keyword, so the second one
|
|
128
|
+
// deduped against the first and vanished — AppSync then rejected the schema for a
|
|
129
|
+
// type that was never emitted.
|
|
130
|
+
let definitionKeywords = ["type ", "enum ", "input ", "union ", "interface ", "scalar "]
|
|
131
|
+
|
|
125
132
|
let extractLeadingName = (str: string): string => {
|
|
126
133
|
let trimmed = str->String.trim
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
trimmed->String.slice(~start=
|
|
130
|
-
|
|
131
|
-
trimmed->String.slice(~start=5, ~end=trimmed->String.length)->String.trim
|
|
132
|
-
} else if trimmed->String.startsWith("input ") {
|
|
133
|
-
trimmed->String.slice(~start=6, ~end=trimmed->String.length)->String.trim
|
|
134
|
-
} else {
|
|
135
|
-
trimmed
|
|
134
|
+
let afterType = switch definitionKeywords->Array.find(kw => trimmed->String.startsWith(kw)) {
|
|
135
|
+
| Some(kw) =>
|
|
136
|
+
trimmed->String.slice(~start=kw->String.length, ~end=trimmed->String.length)->String.trim
|
|
137
|
+
| None => trimmed
|
|
136
138
|
}
|
|
137
139
|
// Split on "(" first to remove arg list, then on " ", "{", and ":" for the name.
|
|
138
140
|
// The ":" split is load-bearing for ARG-LESS fields: `Foo: [Bar!]!` has no "(" to
|
|
@@ -115,11 +115,19 @@ function collectSubscriptionSources(baseFragment, pluginFragments) {
|
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
let definitionKeywords = [
|
|
119
|
+
"type ",
|
|
120
|
+
"enum ",
|
|
121
|
+
"input ",
|
|
122
|
+
"union ",
|
|
123
|
+
"interface ",
|
|
124
|
+
"scalar "
|
|
125
|
+
];
|
|
126
|
+
|
|
118
127
|
function extractLeadingName(str) {
|
|
119
128
|
let trimmed = str.trim();
|
|
120
|
-
let
|
|
121
|
-
|
|
122
|
-
);
|
|
129
|
+
let kw = definitionKeywords.find(kw => trimmed.startsWith(kw));
|
|
130
|
+
let afterType = kw !== undefined ? trimmed.slice(kw.length, trimmed.length).trim() : trimmed;
|
|
123
131
|
return Stdlib_Option.getOr(Stdlib_Option.getOr(Stdlib_Option.getOr(Stdlib_Option.getOr(afterType.split("(")[0], "").trim().split(" ")[0], "").trim().split("{")[0], "").trim().split(":")[0], "").trim();
|
|
124
132
|
}
|
|
125
133
|
|
|
@@ -231,6 +239,7 @@ export {
|
|
|
231
239
|
encode,
|
|
232
240
|
decode,
|
|
233
241
|
collectSubscriptionSources,
|
|
242
|
+
definitionKeywords,
|
|
234
243
|
extractLeadingName,
|
|
235
244
|
relayBaseTypes,
|
|
236
245
|
relayBaseQueries,
|
|
@@ -64,6 +64,51 @@ describe("GraphQL_Stitcher.subscriptionSources", () => {
|
|
|
64
64
|
})
|
|
65
65
|
})
|
|
66
66
|
|
|
67
|
+
// Two unions in one schema is what caught this: every definition keyword the name
|
|
68
|
+
// extractor does not strip makes each declaration of that kind name itself after
|
|
69
|
+
// the keyword, so the second dedupes against the first and is dropped. With
|
|
70
|
+
// `CommandResult` the only union, nothing showed it. The local path does not use
|
|
71
|
+
// this dedup, so a golden carrying both unions still passed while AppSync rejected
|
|
72
|
+
// the schema for a type that was never emitted.
|
|
73
|
+
describe("GraphQL_Stitcher.extractLeadingName", () => {
|
|
74
|
+
testSync("names a union after the union, not after the keyword", () => {
|
|
75
|
+
expect(extractLeadingName("union Geolocation = GeolocationLocated | GeolocationPending"))->toBe(
|
|
76
|
+
"Geolocation",
|
|
77
|
+
)
|
|
78
|
+
expect(extractLeadingName("union CommandResult = CommandAccepted | CommandRejected"))->toBe(
|
|
79
|
+
"CommandResult",
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
testSync("names an interface and a scalar after themselves", () => {
|
|
84
|
+
expect(extractLeadingName("interface Node { id: ID! }"))->toBe("Node")
|
|
85
|
+
expect(extractLeadingName("scalar AWSDateTime"))->toBe("AWSDateTime")
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// The cases that already worked, kept so a rewrite of the keyword list has to
|
|
89
|
+
// keep them working.
|
|
90
|
+
testSync("still names types, enums and inputs", () => {
|
|
91
|
+
expect(extractLeadingName("type Ordering_Customer implements Node { id: ID! }"))->toBe(
|
|
92
|
+
"Ordering_Customer",
|
|
93
|
+
)
|
|
94
|
+
expect(extractLeadingName("enum SortOrder { ASC DESC }"))->toBe("SortOrder")
|
|
95
|
+
expect(extractLeadingName("input Ordering_CustomerFilter { id: ID }"))->toBe(
|
|
96
|
+
"Ordering_CustomerFilter",
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Fields go through the same extractor, and an arg-less one has no "(" to cleave
|
|
101
|
+
// the type off.
|
|
102
|
+
testSync("still names fields, with and without args", () => {
|
|
103
|
+
expect(extractLeadingName(" Catalog_AddProduct(input: AddInput): CommandResult"))->toBe(
|
|
104
|
+
"Catalog_AddProduct",
|
|
105
|
+
)
|
|
106
|
+
expect(extractLeadingName(" Platform_ApiFragments: [Fragment!]!"))->toBe(
|
|
107
|
+
"Platform_ApiFragments",
|
|
108
|
+
)
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
67
112
|
describe("GraphQL_Stitcher.stitchStandalone", () => {
|
|
68
113
|
let pluginFragment = encode({
|
|
69
114
|
types: [
|
|
@@ -77,6 +77,26 @@ globalThis.describe("GraphQL_Stitcher.subscriptionSources", () => {
|
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
globalThis.describe("GraphQL_Stitcher.extractLeadingName", () => {
|
|
81
|
+
globalThis.test("names a union after the union, not after the keyword", () => {
|
|
82
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName("union Geolocation = GeolocationLocated | GeolocationPending")).toBe("Geolocation");
|
|
83
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName("union CommandResult = CommandAccepted | CommandRejected")).toBe("CommandResult");
|
|
84
|
+
});
|
|
85
|
+
globalThis.test("names an interface and a scalar after themselves", () => {
|
|
86
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName("interface Node { id: ID! }")).toBe("Node");
|
|
87
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName("scalar AWSDateTime")).toBe("AWSDateTime");
|
|
88
|
+
});
|
|
89
|
+
globalThis.test("still names types, enums and inputs", () => {
|
|
90
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName("type Ordering_Customer implements Node { id: ID! }")).toBe("Ordering_Customer");
|
|
91
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName("enum SortOrder { ASC DESC }")).toBe("SortOrder");
|
|
92
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName("input Ordering_CustomerFilter { id: ID }")).toBe("Ordering_CustomerFilter");
|
|
93
|
+
});
|
|
94
|
+
globalThis.test("still names fields, with and without args", () => {
|
|
95
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName(" Catalog_AddProduct(input: AddInput): CommandResult")).toBe("Catalog_AddProduct");
|
|
96
|
+
globalThis.expect(GraphQL_Stitcher$ReventlessCore.extractLeadingName(" Platform_ApiFragments: [Fragment!]!")).toBe("Platform_ApiFragments");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
80
100
|
globalThis.describe("GraphQL_Stitcher.stitchStandalone", () => {
|
|
81
101
|
let pluginFragment = GraphQL_Stitcher$ReventlessCore.encode({
|
|
82
102
|
types: [
|