graphql-modules 3.0.0 → 3.1.0-alpha-20260116090824-8472185427836c94f78f09105670ec509014b5d7

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <!-- [![modules](https://user-images.githubusercontent.com/25294569/64067074-ed185b80-cc2a-11e9-8f4d-5f1e19feaa0a.gif)](https://graphql-modules.com/) -->
5
5
 
6
6
  [![npm version](https://badge.fury.io/js/graphql-modules.svg)](https://www.npmjs.com/package/graphql-modules)
7
- ![CI](https://github.com/Urigo/graphql-modules/workflows/CI/badge.svg)
7
+ ![CI](https://github.com/graphql-hive/graphql-modules/workflows/CI/badge.svg)
8
8
  [![Discord Chat](https://img.shields.io/discord/625400653321076807)](https://the-guild.dev/discord)
9
9
  [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg?maxAge=2592000)]()
10
10
 
@@ -0,0 +1,6 @@
1
+ export interface AsyncContext {
2
+ getApplicationContext(): GraphQLModules.AppContext;
3
+ getModuleContext(moduleId: string): GraphQLModules.ModuleContext;
4
+ }
5
+ export declare function getAsyncContext(): AsyncContext | undefined;
6
+ export declare function runWithAsyncContext<R, TArgs extends any[]>(asyncContext: AsyncContext, callback: (...args: TArgs) => R, ...args: TArgs): R;
@@ -3,7 +3,10 @@ import { ResolvedProvider } from '../di/resolution';
3
3
  import type { InternalAppContext, ModulesMap } from './application';
4
4
  export type ExecutionContextBuilder<TContext extends {
5
5
  [key: string]: any;
6
- } = {}> = (context: TContext) => {
6
+ } = {}> = (context: TContext) => ExecutionContextEnv & {
7
+ runWithContext<TReturn = any>(cb: (env: ExecutionContextEnv) => TReturn): TReturn;
8
+ };
9
+ export type ExecutionContextEnv = {
7
10
  context: InternalAppContext;
8
11
  ɵdestroy(): void;
9
12
  ɵinjector: Injector;
package/index.js CHANGED
@@ -4,9 +4,14 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const schema = require('@graphql-tools/schema');
6
6
  const graphql = require('graphql');
7
+ const module$1 = require('module');
7
8
  const wrap = require('@graphql-tools/wrap');
8
9
  const ramda = require('ramda');
9
10
 
11
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
+
13
+ const module__default = /*#__PURE__*/_interopDefaultLegacy(module$1);
14
+
10
15
  const ERROR_ORIGINAL_ERROR = 'diOriginalError';
11
16
  function getOriginalError(error) {
12
17
  return error[ERROR_ORIGINAL_ERROR];
@@ -141,6 +146,7 @@ class InjectionToken {
141
146
  function isType(v) {
142
147
  return typeof v === 'function' && v !== Object;
143
148
  }
149
+ exports.Scope = void 0;
144
150
  (function (Scope) {
145
151
  Scope[Scope["Singleton"] = 0] = "Singleton";
146
152
  Scope[Scope["Operation"] = 1] = "Operation";
@@ -837,6 +843,23 @@ function duplicatedGlobalTokenError(provider, modules) {
837
843
  ].join(' '));
838
844
  }
839
845
 
846
+ let alc;
847
+ if (typeof process !== 'undefined') {
848
+ // probably nodejs runtime
849
+ const require = module__default["default"].createRequire('file:///' /** path is not relevant since we're only loading a builtin */);
850
+ const hooks = require('async_hooks');
851
+ alc = new hooks.AsyncLocalStorage();
852
+ }
853
+ function getAsyncContext() {
854
+ return alc === null || alc === void 0 ? void 0 : alc.getStore();
855
+ }
856
+ function runWithAsyncContext(asyncContext, callback, ...args) {
857
+ if (!alc) {
858
+ return callback(...args);
859
+ }
860
+ return alc.run(asyncContext, callback, ...args);
861
+ }
862
+
840
863
  /**
841
864
  * @api
842
865
  * `CONTEXT` is an InjectionToken representing the provided `GraphQLModules.GlobalContext`
@@ -882,11 +905,14 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
882
905
  },
883
906
  });
884
907
  appInjector.setExecutionContextGetter(function executionContextGetter() {
885
- return appContext;
908
+ var _a;
909
+ return ((_a = getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getApplicationContext()) || appContext;
886
910
  });
887
911
  function createModuleExecutionContextGetter(moduleId) {
888
912
  return function moduleExecutionContextGetter() {
889
- return getModuleContext(moduleId, context);
913
+ var _a;
914
+ return (((_a = getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getModuleContext(moduleId)) ||
915
+ getModuleContext(moduleId, context));
890
916
  };
891
917
  }
892
918
  modulesMap.forEach((mod, moduleId) => {
@@ -956,7 +982,7 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
956
982
  return getModuleContext(moduleId, sharedContext).injector;
957
983
  },
958
984
  });
959
- return {
985
+ const env = {
960
986
  ɵdestroy: once(() => {
961
987
  providersToDestroy.forEach(([injector, keyId]) => {
962
988
  // If provider was instantiated
@@ -970,6 +996,19 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
970
996
  ɵinjector: operationAppInjector,
971
997
  context: sharedContext,
972
998
  };
999
+ return {
1000
+ ...env,
1001
+ runWithContext(cb) {
1002
+ return runWithAsyncContext({
1003
+ getApplicationContext() {
1004
+ return appContext;
1005
+ },
1006
+ getModuleContext(moduleId) {
1007
+ return getModuleContext(moduleId, context);
1008
+ },
1009
+ }, cb, env);
1010
+ },
1011
+ };
973
1012
  };
974
1013
  return contextBuilder;
975
1014
  }
@@ -979,31 +1018,34 @@ function executionCreator({ contextBuilder, }) {
979
1018
  // Custom or original execute function
980
1019
  const executeFn = (options === null || options === void 0 ? void 0 : options.execute) || graphql.execute;
981
1020
  return (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver) => {
982
- var _a;
983
- // Create an execution context
984
- const { context, ɵdestroy: destroy } = (_a = options === null || options === void 0 ? void 0 : options.controller) !== null && _a !== void 0 ? _a : contextBuilder(isNotSchema(argsOrSchema)
1021
+ function perform({ context, ɵdestroy: destroy, }) {
1022
+ const executionArgs = isNotSchema(argsOrSchema)
1023
+ ? {
1024
+ ...argsOrSchema,
1025
+ contextValue: context,
1026
+ }
1027
+ : {
1028
+ schema: argsOrSchema,
1029
+ document: document,
1030
+ rootValue,
1031
+ contextValue: context,
1032
+ variableValues,
1033
+ operationName,
1034
+ fieldResolver,
1035
+ typeResolver,
1036
+ };
1037
+ // It's important to wrap the executeFn within a promise
1038
+ // so we can easily control the end of execution (with finally)
1039
+ return Promise.resolve()
1040
+ .then(() => executeFn(executionArgs))
1041
+ .finally(destroy);
1042
+ }
1043
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1044
+ return perform(options.controller);
1045
+ }
1046
+ return contextBuilder(isNotSchema(argsOrSchema)
985
1047
  ? argsOrSchema.contextValue
986
- : contextValue);
987
- const executionArgs = isNotSchema(argsOrSchema)
988
- ? {
989
- ...argsOrSchema,
990
- contextValue: context,
991
- }
992
- : {
993
- schema: argsOrSchema,
994
- document: document,
995
- rootValue,
996
- contextValue: context,
997
- variableValues,
998
- operationName,
999
- fieldResolver,
1000
- typeResolver,
1001
- };
1002
- // It's important to wrap the executeFn within a promise
1003
- // so we can easily control the end of execution (with finally)
1004
- return Promise.resolve()
1005
- .then(() => executeFn(executionArgs))
1006
- .finally(destroy);
1048
+ : contextValue).runWithContext(perform);
1007
1049
  };
1008
1050
  };
1009
1051
  return createExecution;
@@ -1014,43 +1056,46 @@ function subscriptionCreator({ contextBuilder, }) {
1014
1056
  // Custom or original subscribe function
1015
1057
  const subscribeFn = (options === null || options === void 0 ? void 0 : options.subscribe) || graphql.subscribe;
1016
1058
  return (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, subscribeFieldResolver) => {
1017
- var _a;
1018
- // Create an subscription context
1019
- const { context, ɵdestroy: destroy } = (_a = options === null || options === void 0 ? void 0 : options.controller) !== null && _a !== void 0 ? _a : contextBuilder(isNotSchema(argsOrSchema)
1059
+ function perform({ context, ɵdestroy: destroy, }) {
1060
+ const subscriptionArgs = isNotSchema(argsOrSchema)
1061
+ ? {
1062
+ ...argsOrSchema,
1063
+ contextValue: context,
1064
+ }
1065
+ : {
1066
+ schema: argsOrSchema,
1067
+ document: document,
1068
+ rootValue,
1069
+ contextValue: context,
1070
+ variableValues,
1071
+ operationName,
1072
+ fieldResolver,
1073
+ subscribeFieldResolver,
1074
+ };
1075
+ let isIterable = false;
1076
+ // It's important to wrap the subscribeFn within a promise
1077
+ // so we can easily control the end of subscription (with finally)
1078
+ return Promise.resolve()
1079
+ .then(() => subscribeFn(subscriptionArgs))
1080
+ .then((sub) => {
1081
+ if (isAsyncIterable(sub)) {
1082
+ isIterable = true;
1083
+ return tapAsyncIterator(sub, destroy);
1084
+ }
1085
+ return sub;
1086
+ })
1087
+ .finally(() => {
1088
+ if (!isIterable) {
1089
+ destroy();
1090
+ }
1091
+ });
1092
+ }
1093
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1094
+ return perform(options.controller);
1095
+ }
1096
+ return contextBuilder(isNotSchema(argsOrSchema)
1020
1097
  ? argsOrSchema.contextValue
1021
- : contextValue);
1022
- const subscriptionArgs = isNotSchema(argsOrSchema)
1023
- ? {
1024
- ...argsOrSchema,
1025
- contextValue: context,
1026
- }
1027
- : {
1028
- schema: argsOrSchema,
1029
- document: document,
1030
- rootValue,
1031
- contextValue: context,
1032
- variableValues,
1033
- operationName,
1034
- fieldResolver,
1035
- subscribeFieldResolver,
1036
- };
1037
- let isIterable = false;
1038
- // It's important to wrap the subscribeFn within a promise
1039
- // so we can easily control the end of subscription (with finally)
1040
- return Promise.resolve()
1041
- .then(() => subscribeFn(subscriptionArgs))
1042
- .then((sub) => {
1043
- if (isAsyncIterable(sub)) {
1044
- isIterable = true;
1045
- return tapAsyncIterator(sub, destroy);
1046
- }
1047
- return sub;
1048
- })
1049
- .finally(() => {
1050
- if (!isIterable) {
1051
- destroy();
1052
- }
1053
- });
1098
+ : contextValue).runWithContext(perform);
1054
1099
  };
1055
1100
  };
1056
1101
  return createSubscription;
@@ -1075,10 +1120,9 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1075
1120
  const createApolloSchema = () => {
1076
1121
  const sessions = {};
1077
1122
  const subscription = createSubscription();
1078
- function getSession(ctx) {
1123
+ function getSession(ctx, { context, ɵdestroy: destroy }) {
1079
1124
  if (!ctx[CONTEXT_ID]) {
1080
1125
  ctx[CONTEXT_ID] = uniqueId((id) => !sessions[id]);
1081
- const { context, ɵdestroy: destroy } = contextBuilder(ctx);
1082
1126
  sessions[ctx[CONTEXT_ID]] = {
1083
1127
  count: 0,
1084
1128
  session: {
@@ -1110,20 +1154,22 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1110
1154
  operationName: input.operationName,
1111
1155
  });
1112
1156
  }
1113
- // Create an execution context
1114
- const { context, destroy } = getSession(input.context);
1115
- // It's important to wrap the executeFn within a promise
1116
- // so we can easily control the end of execution (with finally)
1117
- return Promise.resolve()
1118
- .then(() => graphql.execute({
1119
- schema,
1120
- document: input.document,
1121
- contextValue: context,
1122
- variableValues: input.variables,
1123
- rootValue: input.rootValue,
1124
- operationName: input.operationName,
1125
- }))
1126
- .finally(destroy);
1157
+ // Create an execution context and run within it
1158
+ return contextBuilder(input.context).runWithContext((env) => {
1159
+ const { context, destroy } = getSession(input.context, env);
1160
+ // It's important to wrap the executeFn within a promise
1161
+ // so we can easily control the end of execution (with finally)
1162
+ return Promise.resolve()
1163
+ .then(() => graphql.execute({
1164
+ schema,
1165
+ document: input.document,
1166
+ contextValue: context,
1167
+ variableValues: input.variables,
1168
+ rootValue: input.rootValue,
1169
+ operationName: input.operationName,
1170
+ }))
1171
+ .finally(destroy);
1172
+ });
1127
1173
  },
1128
1174
  });
1129
1175
  };
@@ -1545,6 +1591,12 @@ function createResolvers(config, metadata, app) {
1545
1591
  });
1546
1592
  resolvers[typeName][fieldName].subscribe = resolver;
1547
1593
  }
1594
+ if (isDefined(obj[fieldName].extensions)) {
1595
+ // Do NOT add a resolve if one is not specified, it will cause
1596
+ // change in behavior in systems like `grafast`
1597
+ resolvers[typeName][fieldName].extensions =
1598
+ obj[fieldName].extensions;
1599
+ }
1548
1600
  }
1549
1601
  }
1550
1602
  }
@@ -1665,6 +1717,14 @@ function addObject({ typeName, fields, container, config, }) {
1665
1717
  writeResolverMetadata(resolver.subscribe, config);
1666
1718
  container[typeName][fieldName].subscribe = resolver.subscribe;
1667
1719
  }
1720
+ // extensions
1721
+ if (isDefined(resolver.extensions)) {
1722
+ if (container[typeName][fieldName].extensions) {
1723
+ throw new ResolverDuplicatedError(`Duplicated resolver of "${typeName}.${fieldName}" (extensions object)`, useLocation({ dirname: config.dirname, id: config.id }));
1724
+ }
1725
+ writeResolverMetadata(resolver.extensions, config);
1726
+ container[typeName][fieldName].extensions = resolver.extensions;
1727
+ }
1668
1728
  }
1669
1729
  }
1670
1730
  }
@@ -1795,7 +1855,9 @@ function isResolveFn(value) {
1795
1855
  return typeof value === 'function';
1796
1856
  }
1797
1857
  function isResolveOptions(value) {
1798
- return isDefined(value.resolve) || isDefined(value.subscribe);
1858
+ return (isDefined(value.resolve) ||
1859
+ isDefined(value.subscribe) ||
1860
+ isDefined(value.extensions));
1799
1861
  }
1800
1862
  function isScalarResolver(obj) {
1801
1863
  return obj instanceof graphql.GraphQLScalarType;
package/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { makeExecutableSchema } from '@graphql-tools/schema';
2
- import { GraphQLSchema, execute as execute$1, subscribe, visit, Kind, GraphQLScalarType, concatAST, defaultFieldResolver, parse } from 'graphql';
2
+ import { GraphQLSchema, execute as execute$1, subscribe, visit, Kind, concatAST, defaultFieldResolver, GraphQLScalarType, parse } from 'graphql';
3
+ import module from 'module';
3
4
  import { wrapSchema } from '@graphql-tools/wrap';
4
5
  import { mergeDeepWith } from 'ramda';
5
6
 
@@ -834,6 +835,23 @@ function duplicatedGlobalTokenError(provider, modules) {
834
835
  ].join(' '));
835
836
  }
836
837
 
838
+ let alc;
839
+ if (typeof process !== 'undefined') {
840
+ // probably nodejs runtime
841
+ const require = module.createRequire('file:///' /** path is not relevant since we're only loading a builtin */);
842
+ const hooks = require('async_hooks');
843
+ alc = new hooks.AsyncLocalStorage();
844
+ }
845
+ function getAsyncContext() {
846
+ return alc === null || alc === void 0 ? void 0 : alc.getStore();
847
+ }
848
+ function runWithAsyncContext(asyncContext, callback, ...args) {
849
+ if (!alc) {
850
+ return callback(...args);
851
+ }
852
+ return alc.run(asyncContext, callback, ...args);
853
+ }
854
+
837
855
  /**
838
856
  * @api
839
857
  * `CONTEXT` is an InjectionToken representing the provided `GraphQLModules.GlobalContext`
@@ -879,11 +897,14 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
879
897
  },
880
898
  });
881
899
  appInjector.setExecutionContextGetter(function executionContextGetter() {
882
- return appContext;
900
+ var _a;
901
+ return ((_a = getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getApplicationContext()) || appContext;
883
902
  });
884
903
  function createModuleExecutionContextGetter(moduleId) {
885
904
  return function moduleExecutionContextGetter() {
886
- return getModuleContext(moduleId, context);
905
+ var _a;
906
+ return (((_a = getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getModuleContext(moduleId)) ||
907
+ getModuleContext(moduleId, context));
887
908
  };
888
909
  }
889
910
  modulesMap.forEach((mod, moduleId) => {
@@ -953,7 +974,7 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
953
974
  return getModuleContext(moduleId, sharedContext).injector;
954
975
  },
955
976
  });
956
- return {
977
+ const env = {
957
978
  ɵdestroy: once(() => {
958
979
  providersToDestroy.forEach(([injector, keyId]) => {
959
980
  // If provider was instantiated
@@ -967,6 +988,19 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
967
988
  ɵinjector: operationAppInjector,
968
989
  context: sharedContext,
969
990
  };
991
+ return {
992
+ ...env,
993
+ runWithContext(cb) {
994
+ return runWithAsyncContext({
995
+ getApplicationContext() {
996
+ return appContext;
997
+ },
998
+ getModuleContext(moduleId) {
999
+ return getModuleContext(moduleId, context);
1000
+ },
1001
+ }, cb, env);
1002
+ },
1003
+ };
970
1004
  };
971
1005
  return contextBuilder;
972
1006
  }
@@ -976,31 +1010,34 @@ function executionCreator({ contextBuilder, }) {
976
1010
  // Custom or original execute function
977
1011
  const executeFn = (options === null || options === void 0 ? void 0 : options.execute) || execute$1;
978
1012
  return (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver) => {
979
- var _a;
980
- // Create an execution context
981
- const { context, ɵdestroy: destroy } = (_a = options === null || options === void 0 ? void 0 : options.controller) !== null && _a !== void 0 ? _a : contextBuilder(isNotSchema(argsOrSchema)
1013
+ function perform({ context, ɵdestroy: destroy, }) {
1014
+ const executionArgs = isNotSchema(argsOrSchema)
1015
+ ? {
1016
+ ...argsOrSchema,
1017
+ contextValue: context,
1018
+ }
1019
+ : {
1020
+ schema: argsOrSchema,
1021
+ document: document,
1022
+ rootValue,
1023
+ contextValue: context,
1024
+ variableValues,
1025
+ operationName,
1026
+ fieldResolver,
1027
+ typeResolver,
1028
+ };
1029
+ // It's important to wrap the executeFn within a promise
1030
+ // so we can easily control the end of execution (with finally)
1031
+ return Promise.resolve()
1032
+ .then(() => executeFn(executionArgs))
1033
+ .finally(destroy);
1034
+ }
1035
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1036
+ return perform(options.controller);
1037
+ }
1038
+ return contextBuilder(isNotSchema(argsOrSchema)
982
1039
  ? argsOrSchema.contextValue
983
- : contextValue);
984
- const executionArgs = isNotSchema(argsOrSchema)
985
- ? {
986
- ...argsOrSchema,
987
- contextValue: context,
988
- }
989
- : {
990
- schema: argsOrSchema,
991
- document: document,
992
- rootValue,
993
- contextValue: context,
994
- variableValues,
995
- operationName,
996
- fieldResolver,
997
- typeResolver,
998
- };
999
- // It's important to wrap the executeFn within a promise
1000
- // so we can easily control the end of execution (with finally)
1001
- return Promise.resolve()
1002
- .then(() => executeFn(executionArgs))
1003
- .finally(destroy);
1040
+ : contextValue).runWithContext(perform);
1004
1041
  };
1005
1042
  };
1006
1043
  return createExecution;
@@ -1011,43 +1048,46 @@ function subscriptionCreator({ contextBuilder, }) {
1011
1048
  // Custom or original subscribe function
1012
1049
  const subscribeFn = (options === null || options === void 0 ? void 0 : options.subscribe) || subscribe;
1013
1050
  return (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, subscribeFieldResolver) => {
1014
- var _a;
1015
- // Create an subscription context
1016
- const { context, ɵdestroy: destroy } = (_a = options === null || options === void 0 ? void 0 : options.controller) !== null && _a !== void 0 ? _a : contextBuilder(isNotSchema(argsOrSchema)
1051
+ function perform({ context, ɵdestroy: destroy, }) {
1052
+ const subscriptionArgs = isNotSchema(argsOrSchema)
1053
+ ? {
1054
+ ...argsOrSchema,
1055
+ contextValue: context,
1056
+ }
1057
+ : {
1058
+ schema: argsOrSchema,
1059
+ document: document,
1060
+ rootValue,
1061
+ contextValue: context,
1062
+ variableValues,
1063
+ operationName,
1064
+ fieldResolver,
1065
+ subscribeFieldResolver,
1066
+ };
1067
+ let isIterable = false;
1068
+ // It's important to wrap the subscribeFn within a promise
1069
+ // so we can easily control the end of subscription (with finally)
1070
+ return Promise.resolve()
1071
+ .then(() => subscribeFn(subscriptionArgs))
1072
+ .then((sub) => {
1073
+ if (isAsyncIterable(sub)) {
1074
+ isIterable = true;
1075
+ return tapAsyncIterator(sub, destroy);
1076
+ }
1077
+ return sub;
1078
+ })
1079
+ .finally(() => {
1080
+ if (!isIterable) {
1081
+ destroy();
1082
+ }
1083
+ });
1084
+ }
1085
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1086
+ return perform(options.controller);
1087
+ }
1088
+ return contextBuilder(isNotSchema(argsOrSchema)
1017
1089
  ? argsOrSchema.contextValue
1018
- : contextValue);
1019
- const subscriptionArgs = isNotSchema(argsOrSchema)
1020
- ? {
1021
- ...argsOrSchema,
1022
- contextValue: context,
1023
- }
1024
- : {
1025
- schema: argsOrSchema,
1026
- document: document,
1027
- rootValue,
1028
- contextValue: context,
1029
- variableValues,
1030
- operationName,
1031
- fieldResolver,
1032
- subscribeFieldResolver,
1033
- };
1034
- let isIterable = false;
1035
- // It's important to wrap the subscribeFn within a promise
1036
- // so we can easily control the end of subscription (with finally)
1037
- return Promise.resolve()
1038
- .then(() => subscribeFn(subscriptionArgs))
1039
- .then((sub) => {
1040
- if (isAsyncIterable(sub)) {
1041
- isIterable = true;
1042
- return tapAsyncIterator(sub, destroy);
1043
- }
1044
- return sub;
1045
- })
1046
- .finally(() => {
1047
- if (!isIterable) {
1048
- destroy();
1049
- }
1050
- });
1090
+ : contextValue).runWithContext(perform);
1051
1091
  };
1052
1092
  };
1053
1093
  return createSubscription;
@@ -1072,10 +1112,9 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1072
1112
  const createApolloSchema = () => {
1073
1113
  const sessions = {};
1074
1114
  const subscription = createSubscription();
1075
- function getSession(ctx) {
1115
+ function getSession(ctx, { context, ɵdestroy: destroy }) {
1076
1116
  if (!ctx[CONTEXT_ID]) {
1077
1117
  ctx[CONTEXT_ID] = uniqueId((id) => !sessions[id]);
1078
- const { context, ɵdestroy: destroy } = contextBuilder(ctx);
1079
1118
  sessions[ctx[CONTEXT_ID]] = {
1080
1119
  count: 0,
1081
1120
  session: {
@@ -1107,20 +1146,22 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1107
1146
  operationName: input.operationName,
1108
1147
  });
1109
1148
  }
1110
- // Create an execution context
1111
- const { context, destroy } = getSession(input.context);
1112
- // It's important to wrap the executeFn within a promise
1113
- // so we can easily control the end of execution (with finally)
1114
- return Promise.resolve()
1115
- .then(() => execute$1({
1116
- schema,
1117
- document: input.document,
1118
- contextValue: context,
1119
- variableValues: input.variables,
1120
- rootValue: input.rootValue,
1121
- operationName: input.operationName,
1122
- }))
1123
- .finally(destroy);
1149
+ // Create an execution context and run within it
1150
+ return contextBuilder(input.context).runWithContext((env) => {
1151
+ const { context, destroy } = getSession(input.context, env);
1152
+ // It's important to wrap the executeFn within a promise
1153
+ // so we can easily control the end of execution (with finally)
1154
+ return Promise.resolve()
1155
+ .then(() => execute$1({
1156
+ schema,
1157
+ document: input.document,
1158
+ contextValue: context,
1159
+ variableValues: input.variables,
1160
+ rootValue: input.rootValue,
1161
+ operationName: input.operationName,
1162
+ }))
1163
+ .finally(destroy);
1164
+ });
1124
1165
  },
1125
1166
  });
1126
1167
  };
@@ -1542,6 +1583,12 @@ function createResolvers(config, metadata, app) {
1542
1583
  });
1543
1584
  resolvers[typeName][fieldName].subscribe = resolver;
1544
1585
  }
1586
+ if (isDefined(obj[fieldName].extensions)) {
1587
+ // Do NOT add a resolve if one is not specified, it will cause
1588
+ // change in behavior in systems like `grafast`
1589
+ resolvers[typeName][fieldName].extensions =
1590
+ obj[fieldName].extensions;
1591
+ }
1545
1592
  }
1546
1593
  }
1547
1594
  }
@@ -1662,6 +1709,14 @@ function addObject({ typeName, fields, container, config, }) {
1662
1709
  writeResolverMetadata(resolver.subscribe, config);
1663
1710
  container[typeName][fieldName].subscribe = resolver.subscribe;
1664
1711
  }
1712
+ // extensions
1713
+ if (isDefined(resolver.extensions)) {
1714
+ if (container[typeName][fieldName].extensions) {
1715
+ throw new ResolverDuplicatedError(`Duplicated resolver of "${typeName}.${fieldName}" (extensions object)`, useLocation({ dirname: config.dirname, id: config.id }));
1716
+ }
1717
+ writeResolverMetadata(resolver.extensions, config);
1718
+ container[typeName][fieldName].extensions = resolver.extensions;
1719
+ }
1665
1720
  }
1666
1721
  }
1667
1722
  }
@@ -1792,7 +1847,9 @@ function isResolveFn(value) {
1792
1847
  return typeof value === 'function';
1793
1848
  }
1794
1849
  function isResolveOptions(value) {
1795
- return isDefined(value.resolve) || isDefined(value.subscribe);
1850
+ return (isDefined(value.resolve) ||
1851
+ isDefined(value.subscribe) ||
1852
+ isDefined(value.extensions));
1796
1853
  }
1797
1854
  function isScalarResolver(obj) {
1798
1855
  return obj instanceof GraphQLScalarType;
@@ -1,3 +1,4 @@
1
+ import { GraphQLFieldExtensions } from 'graphql';
1
2
  import { ModuleConfig } from './types';
2
3
  import { ModuleMetadata } from './metadata';
3
4
  import { ResolveFn, ID } from './../shared/types';
@@ -8,5 +9,5 @@ interface ResolverMetadata {
8
9
  export declare function createResolvers(config: ModuleConfig, metadata: ModuleMetadata, app: {
9
10
  middlewareMap: MiddlewareMap;
10
11
  }): Record<string, any>;
11
- export declare function readResolverMetadata(resolver: ResolveFn): ResolverMetadata;
12
+ export declare function readResolverMetadata(resolver: ResolveFn | GraphQLFieldExtensions<any, any, any>): ResolverMetadata;
12
13
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-modules",
3
- "version": "3.0.0",
3
+ "version": "3.1.0-alpha-20260116090824-8472185427836c94f78f09105670ec509014b5d7",
4
4
  "description": "Create reusable, maintainable, testable and extendable GraphQL modules",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {