@graphql-tools/stitch 10.3.0 → 10.3.1

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,5 +1,18 @@
1
1
  # @graphql-tools/stitch
2
2
 
3
+ ## 10.3.1
4
+ ### Patch Changes
5
+
6
+
7
+
8
+ - [#2591](https://github.com/graphql-hive/gateway/pull/2591) [`8f582af`](https://github.com/graphql-hive/gateway/commit/8f582af7e2cd250d9538495487c33a64b038bcb4) Thanks [@kpiotr](https://github.com/kpiotr)! - `stitchSchemas` no longer mutates the resolver objects passed in `resolvers`.
9
+
10
+ The wrapper it adds around resolvers of fields that return a merged type (so the merged type's fields get resolved) used to be written back into the caller's field config (`existing.resolve = wrappedResolve`). A resolver map reused across `stitchSchemas` calls — a gateway's additional resolvers on every supergraph reload — therefore gained one wrapper per call, and every wrapper kept its call's `stitchingInfo`, and with it the entire superseded schema, subschemas and executors, reachable: a heap leak of one schema generation per reload.
11
+
12
+ Nesting also changed behaviour, because `wrappedResolve` closes over its own call's `stitchingInfo` and the innermost (oldest) wrapper runs first: such a field hydrated through a superseded call's stitching plan and executors, and once that hydration satisfied the selection the newest wrapper found nothing left to resolve.
13
+
14
+ The merged resolver map is now cloned before the wrappers are added. Enum value maps, scalars, union and input type resolvers and plain resolver functions are still passed through by reference, so their identities are unchanged.
15
+
3
16
  ## 10.3.0
4
17
  ### Minor Changes
5
18
 
package/dist/index.cjs CHANGED
@@ -3234,7 +3234,10 @@ function stitchSchemas(options) {
3234
3234
  commentDescriptions: true
3235
3235
  });
3236
3236
  }
3237
- const resolverMap = merge.mergeResolvers(resolvers);
3237
+ const resolverMap = cloneResolverMap(
3238
+ merge.mergeResolvers(resolvers),
3239
+ schema$1
3240
+ );
3238
3241
  const finalResolvers = inheritResolversFromInterfaces ? schema.extendResolversFromInterfaces(schema$1, resolverMap) : resolverMap;
3239
3242
  stitchingInfo = completeStitchingInfo(stitchingInfo, finalResolvers, schema$1);
3240
3243
  addLocalFieldResolvers(schema$1, finalResolvers, stitchingInfo, typeCandidates);
@@ -3301,6 +3304,31 @@ function stripCustomDirectiveUsages(types) {
3301
3304
  }
3302
3305
  }
3303
3306
  }
3307
+ function isResolverRecord(value) {
3308
+ return value != null && typeof value === "object" && !Array.isArray(value);
3309
+ }
3310
+ function cloneResolverMap(resolverMap, schema) {
3311
+ const cloned = {};
3312
+ for (const typeName in resolverMap) {
3313
+ const typeResolvers = resolverMap[typeName];
3314
+ if (typeResolvers === void 0) {
3315
+ continue;
3316
+ }
3317
+ const type = schema.getType(typeName);
3318
+ if (!isResolverRecord(typeResolvers) || !graphql.isObjectType(type) && !graphql.isInterfaceType(type)) {
3319
+ cloned[typeName] = typeResolvers;
3320
+ continue;
3321
+ }
3322
+ const fieldConfigs = typeResolvers;
3323
+ const clonedType = {};
3324
+ for (const fieldName in fieldConfigs) {
3325
+ const field = fieldConfigs[fieldName];
3326
+ clonedType[fieldName] = isResolverRecord(field) ? { ...field } : field;
3327
+ }
3328
+ cloned[typeName] = clonedType;
3329
+ }
3330
+ return cloned;
3331
+ }
3304
3332
  function addLocalFieldResolvers(schema, resolvers, stitchingInfo, typeCandidates) {
3305
3333
  const subschemaFields = /* @__PURE__ */ new Set();
3306
3334
  const localFields = /* @__PURE__ */ new Map();
package/dist/index.js CHANGED
@@ -3232,7 +3232,10 @@ function stitchSchemas(options) {
3232
3232
  commentDescriptions: true
3233
3233
  });
3234
3234
  }
3235
- const resolverMap = mergeResolvers(resolvers);
3235
+ const resolverMap = cloneResolverMap(
3236
+ mergeResolvers(resolvers),
3237
+ schema
3238
+ );
3236
3239
  const finalResolvers = inheritResolversFromInterfaces ? extendResolversFromInterfaces(schema, resolverMap) : resolverMap;
3237
3240
  stitchingInfo = completeStitchingInfo(stitchingInfo, finalResolvers, schema);
3238
3241
  addLocalFieldResolvers(schema, finalResolvers, stitchingInfo, typeCandidates);
@@ -3299,6 +3302,31 @@ function stripCustomDirectiveUsages(types) {
3299
3302
  }
3300
3303
  }
3301
3304
  }
3305
+ function isResolverRecord(value) {
3306
+ return value != null && typeof value === "object" && !Array.isArray(value);
3307
+ }
3308
+ function cloneResolverMap(resolverMap, schema) {
3309
+ const cloned = {};
3310
+ for (const typeName in resolverMap) {
3311
+ const typeResolvers = resolverMap[typeName];
3312
+ if (typeResolvers === void 0) {
3313
+ continue;
3314
+ }
3315
+ const type = schema.getType(typeName);
3316
+ if (!isResolverRecord(typeResolvers) || !isObjectType(type) && !isInterfaceType(type)) {
3317
+ cloned[typeName] = typeResolvers;
3318
+ continue;
3319
+ }
3320
+ const fieldConfigs = typeResolvers;
3321
+ const clonedType = {};
3322
+ for (const fieldName in fieldConfigs) {
3323
+ const field = fieldConfigs[fieldName];
3324
+ clonedType[fieldName] = isResolverRecord(field) ? { ...field } : field;
3325
+ }
3326
+ cloned[typeName] = clonedType;
3327
+ }
3328
+ return cloned;
3329
+ }
3302
3330
  function addLocalFieldResolvers(schema, resolvers, stitchingInfo, typeCandidates) {
3303
3331
  const subschemaFields = /* @__PURE__ */ new Set();
3304
3332
  const localFields = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-tools/stitch",
3
- "version": "10.3.0",
3
+ "version": "10.3.1",
4
4
  "type": "module",
5
5
  "description": "A set of utils for faster development of GraphQL tools",
6
6
  "repository": {