@trackunit/graphql-build-tools 0.0.18-alpha-6a85feaada3.0 → 0.0.18
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
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## 0.0.18 (2026-06-29)
|
|
2
|
+
|
|
3
|
+
This was a version bump only for graphql-build-tools to align it with other projects, there were no code changes.
|
|
4
|
+
|
|
5
|
+
## 0.0.17 (2026-06-25)
|
|
6
|
+
|
|
7
|
+
This was a version bump only for graphql-build-tools to align it with other projects, there were no code changes.
|
|
8
|
+
|
|
9
|
+
## 0.0.16 (2026-06-24)
|
|
10
|
+
|
|
11
|
+
### 🩹 Fixes
|
|
12
|
+
|
|
13
|
+
- **graphql-build-tools:** preserve schema directives and drop emptied root types when reconciling root operations ([fdc403007aa](https://github.com/Trackunit/manager/commit/fdc403007aa))
|
|
14
|
+
- **graphql-build-tools:** drop dangling root operation bindings when @hidden empties a root type ([af908369e9f](https://github.com/Trackunit/manager/commit/af908369e9f))
|
|
15
|
+
|
|
16
|
+
### ❤️ Thank You
|
|
17
|
+
|
|
18
|
+
- Claude Opus 4.8
|
|
19
|
+
- Cursor @cursoragent
|
|
20
|
+
- Kieran Prince
|
|
21
|
+
- Ulrik Jørgensen
|
|
22
|
+
|
|
1
23
|
## 0.0.15 (2026-06-23)
|
|
2
24
|
|
|
3
25
|
### 🩹 Fixes
|
package/package.json
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reconcileRootOperationTypes = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
/**
|
|
6
|
+
* Per-server @hidden filtering can prune a root operation type (query/mutation/subscription) away while
|
|
7
|
+
* the original explicit `schema {}` block still binds it. `printSchemaWithDirectives` then emits a
|
|
8
|
+
* dangling binding (e.g. `mutation: Mutation` with no `type Mutation`), which fails stitching
|
|
9
|
+
* composition with `Unknown type Mutation.`. A subgraph legitimately having no query or no mutation for
|
|
10
|
+
* a server is fine — only the dangling reference is not.
|
|
11
|
+
*
|
|
12
|
+
* This rebinds each operation to a live, non-empty root type and rewrites the schema-definition AST so it
|
|
13
|
+
* only lists operations whose root type is still live. Schema-level directives / extensions on that AST
|
|
14
|
+
* are preserved (they live nowhere else), and any now-empty root type is dropped from the type map so we
|
|
15
|
+
* never emit an empty `type X {}`. Returns the schema untouched when nothing dangles, keeping the common
|
|
16
|
+
* case byte-for-byte identical.
|
|
17
|
+
*/
|
|
18
|
+
const isNonEmptyObjectType = (type) => type !== null && type !== undefined && Object.keys(type.getFields()).length > 0;
|
|
19
|
+
// Operations still bound by the schema-definition AST (what astFromSchema would print).
|
|
20
|
+
const boundOperationsFromAst = (schema) => {
|
|
21
|
+
const operations = new Set();
|
|
22
|
+
const nodes = [schema.astNode, ...schema.extensionASTNodes];
|
|
23
|
+
for (const node of nodes) {
|
|
24
|
+
if (node === null || node === undefined) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
for (const operationType of node.operationTypes ?? []) {
|
|
28
|
+
operations.add(operationType.operation);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return operations;
|
|
32
|
+
};
|
|
33
|
+
// Keep only the operation bindings whose root type is still live, dropping the dangling ones.
|
|
34
|
+
const liveOperationTypeNodes = (operationTypes, liveOperations) => (operationTypes ?? []).filter(operationType => liveOperations.has(operationType.operation));
|
|
35
|
+
const reconcileRootOperationTypes = (schema) => {
|
|
36
|
+
const config = schema.toConfig();
|
|
37
|
+
const liveQuery = isNonEmptyObjectType(config.query) ? config.query : undefined;
|
|
38
|
+
const liveMutation = isNonEmptyObjectType(config.mutation) ? config.mutation : undefined;
|
|
39
|
+
const liveSubscription = isNonEmptyObjectType(config.subscription) ? config.subscription : undefined;
|
|
40
|
+
const emptiedAnOperation = liveQuery !== config.query || liveMutation !== config.mutation || liveSubscription !== config.subscription;
|
|
41
|
+
const boundOperations = boundOperationsFromAst(schema);
|
|
42
|
+
const hasDanglingBinding = (boundOperations.has(graphql_1.OperationTypeNode.QUERY) && liveQuery === undefined) ||
|
|
43
|
+
(boundOperations.has(graphql_1.OperationTypeNode.MUTATION) && liveMutation === undefined) ||
|
|
44
|
+
(boundOperations.has(graphql_1.OperationTypeNode.SUBSCRIPTION) && liveSubscription === undefined);
|
|
45
|
+
// No emptied root and no dangling binding — leave the output identical.
|
|
46
|
+
if (!emptiedAnOperation && !hasDanglingBinding) {
|
|
47
|
+
return schema;
|
|
48
|
+
}
|
|
49
|
+
const liveOperations = new Set();
|
|
50
|
+
if (liveQuery !== undefined) {
|
|
51
|
+
liveOperations.add(graphql_1.OperationTypeNode.QUERY);
|
|
52
|
+
}
|
|
53
|
+
if (liveMutation !== undefined) {
|
|
54
|
+
liveOperations.add(graphql_1.OperationTypeNode.MUTATION);
|
|
55
|
+
}
|
|
56
|
+
if (liveSubscription !== undefined) {
|
|
57
|
+
liveOperations.add(graphql_1.OperationTypeNode.SUBSCRIPTION);
|
|
58
|
+
}
|
|
59
|
+
// Root types that are no longer live must leave the type map, otherwise an empty `type X {}` is printed.
|
|
60
|
+
const deadRootTypeNames = new Set();
|
|
61
|
+
const markDeadRoot = (original, live) => {
|
|
62
|
+
if (original !== null && original !== undefined && live === undefined) {
|
|
63
|
+
deadRootTypeNames.add(original.name);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
markDeadRoot(config.query, liveQuery);
|
|
67
|
+
markDeadRoot(config.mutation, liveMutation);
|
|
68
|
+
markDeadRoot(config.subscription, liveSubscription);
|
|
69
|
+
const reconciledAstNode = config.astNode === null || config.astNode === undefined
|
|
70
|
+
? config.astNode
|
|
71
|
+
: { ...config.astNode, operationTypes: liveOperationTypeNodes(config.astNode.operationTypes, liveOperations) };
|
|
72
|
+
return new graphql_1.GraphQLSchema({
|
|
73
|
+
...config,
|
|
74
|
+
query: liveQuery,
|
|
75
|
+
mutation: liveMutation,
|
|
76
|
+
subscription: liveSubscription,
|
|
77
|
+
// Drop dead roots from the explicit type list. `new GraphQLSchema` re-collects any type still
|
|
78
|
+
// referenced by a live field, so a former root that is also used as a field type survives as a
|
|
79
|
+
// regular type — only the genuinely unreferenced root is removed.
|
|
80
|
+
types: config.types.filter(type => !deadRootTypeNames.has(type.name)),
|
|
81
|
+
// Keep schema-level directives/extensions; only strip operation entries whose root type is gone.
|
|
82
|
+
astNode: reconciledAstNode,
|
|
83
|
+
extensionASTNodes: config.extensionASTNodes.map(node => ({
|
|
84
|
+
...node,
|
|
85
|
+
operationTypes: liveOperationTypeNodes(node.operationTypes, liveOperations),
|
|
86
|
+
})),
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
exports.reconcileRootOperationTypes = reconcileRootOperationTypes;
|
|
90
|
+
//# sourceMappingURL=reconcile-root-operation-types.js.map
|
|
@@ -8,6 +8,7 @@ const complexity_directive_1 = require("./complexity-directive");
|
|
|
8
8
|
const global_id_transformer_1 = require("./global-id-transformer");
|
|
9
9
|
const hidden_directive_1 = require("./hidden-directive");
|
|
10
10
|
const preview_directive_1 = require("./preview-directive");
|
|
11
|
+
const reconcile_root_operation_types_1 = require("./reconcile-root-operation-types");
|
|
11
12
|
exports.graphqlGatewayNames = ["internal", "public", "report", "v3-mobile"];
|
|
12
13
|
const applySdlDirectiveTransformers = (schema, gatewayName, { preserveStitchingEntrypoints = false } = {}) => {
|
|
13
14
|
const { complexityDirectiveTransformer } = (0, complexity_directive_1.complexityDirective)();
|
|
@@ -16,7 +17,10 @@ const applySdlDirectiveTransformers = (schema, gatewayName, { preserveStitchingE
|
|
|
16
17
|
const { authorizationDirectiveTransformer } = (0, authorization_directive_1.authorizationDirective)();
|
|
17
18
|
const hiddenSchema = hiddenDirectiveTransformer(schema);
|
|
18
19
|
const previewSchema = gatewayName === "report" ? hiddenSchema : previewDirectiveTransformer(hiddenSchema);
|
|
19
|
-
|
|
20
|
+
const transformedSchema = authorizationDirectiveTransformer(complexityDirectiveTransformer((0, global_id_transformer_1.globalIdTransformer)(previewSchema)));
|
|
21
|
+
// Drop dangling/empty root operations left when @hidden empties a root type. Complements
|
|
22
|
+
// preserveStitchingEntrypoints, which only rescues roots whose fields carry a stitching directive.
|
|
23
|
+
return (0, reconcile_root_operation_types_1.reconcileRootOperationTypes)(transformedSchema);
|
|
20
24
|
};
|
|
21
25
|
exports.applySdlDirectiveTransformers = applySdlDirectiveTransformers;
|
|
22
26
|
exports.applyDirectiveTransformers = exports.applySdlDirectiveTransformers;
|