graphql-modules 3.1.0 → 3.1.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/README.md CHANGED
@@ -1,7 +1,4 @@
1
- [![GraphQLConf 2024 Banner: September 10-12, San Francisco. Hosted by the GraphQL Foundation](https://github.com/user-attachments/assets/bdb8cd5d-5186-4ece-b06b-b00a499b7868)](https://graphql.org/conf/2024/?utm_source=github&utm_medium=graphql_modules&utm_campaign=readme)
2
-
3
- <!-- Uncomment when we remove GraphQL Conf banner -->
4
- <!-- [![modules](https://user-images.githubusercontent.com/25294569/64067074-ed185b80-cc2a-11e9-8f4d-5f1e19feaa0a.gif)](https://graphql-modules.com/) -->
1
+ [![modules](https://user-images.githubusercontent.com/25294569/64067074-ed185b80-cc2a-11e9-8f4d-5f1e19feaa0a.gif)](https://graphql-modules.com/)
5
2
 
6
3
  [![npm version](https://badge.fury.io/js/graphql-modules.svg)](https://www.npmjs.com/package/graphql-modules)
7
4
  ![CI](https://github.com/graphql-hive/graphql-modules/workflows/CI/badge.svg)
@@ -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;
@@ -0,0 +1,7 @@
1
+ // noop
2
+ // TODO: typecheck
3
+
4
+ export default {
5
+ getAsyncContext: () => undefined,
6
+ runWithAsyncContext: (_asyncContext, callback, ...args) => callback(...args),
7
+ };
@@ -0,0 +1,14 @@
1
+ // use async hooks
2
+ // TODO: typecheck
3
+
4
+ const hooks = require('async_hooks');
5
+
6
+ const alc = new hooks.AsyncLocalStorage();
7
+
8
+ // we dont use named exports (exports.getAsyncContext) because node with typescript is weird
9
+
10
+ module.exports = {
11
+ getAsyncContext: () => alc.getStore(),
12
+ runWithAsyncContext: (asyncContext, callback, ...args) =>
13
+ alc.run(asyncContext, callback, ...args),
14
+ };
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 async_context = require('#async-context');
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 async_context__default = /*#__PURE__*/_interopDefaultLegacy(async_context);
14
+
10
15
  const ERROR_ORIGINAL_ERROR = 'diOriginalError';
11
16
  function getOriginalError(error) {
12
17
  return error[ERROR_ORIGINAL_ERROR];
@@ -883,11 +888,14 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
883
888
  },
884
889
  });
885
890
  appInjector.setExecutionContextGetter(function executionContextGetter() {
886
- return appContext;
891
+ var _a;
892
+ return (((_a = async_context__default["default"].getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getApplicationContext()) || appContext);
887
893
  });
888
894
  function createModuleExecutionContextGetter(moduleId) {
889
895
  return function moduleExecutionContextGetter() {
890
- return getModuleContext(moduleId, context);
896
+ var _a;
897
+ return (((_a = async_context__default["default"].getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getModuleContext(moduleId)) ||
898
+ getModuleContext(moduleId, context));
891
899
  };
892
900
  }
893
901
  modulesMap.forEach((mod, moduleId) => {
@@ -957,7 +965,7 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
957
965
  return getModuleContext(moduleId, sharedContext).injector;
958
966
  },
959
967
  });
960
- return {
968
+ const env = {
961
969
  ɵdestroy: once(() => {
962
970
  providersToDestroy.forEach(([injector, keyId]) => {
963
971
  // If provider was instantiated
@@ -971,6 +979,19 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
971
979
  ɵinjector: operationAppInjector,
972
980
  context: sharedContext,
973
981
  };
982
+ return {
983
+ ...env,
984
+ runWithContext(cb) {
985
+ return async_context__default["default"].runWithAsyncContext({
986
+ getApplicationContext() {
987
+ return appContext;
988
+ },
989
+ getModuleContext(moduleId) {
990
+ return getModuleContext(moduleId, context);
991
+ },
992
+ }, cb, env);
993
+ },
994
+ };
974
995
  };
975
996
  return contextBuilder;
976
997
  }
@@ -980,31 +1001,34 @@ function executionCreator({ contextBuilder, }) {
980
1001
  // Custom or original execute function
981
1002
  const executeFn = (options === null || options === void 0 ? void 0 : options.execute) || graphql.execute;
982
1003
  return (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, typeResolver) => {
983
- var _a;
984
- // Create an execution context
985
- const { context, ɵdestroy: destroy } = (_a = options === null || options === void 0 ? void 0 : options.controller) !== null && _a !== void 0 ? _a : contextBuilder(isNotSchema(argsOrSchema)
1004
+ function perform({ context, ɵdestroy: destroy, }) {
1005
+ const executionArgs = isNotSchema(argsOrSchema)
1006
+ ? {
1007
+ ...argsOrSchema,
1008
+ contextValue: context,
1009
+ }
1010
+ : {
1011
+ schema: argsOrSchema,
1012
+ document: document,
1013
+ rootValue,
1014
+ contextValue: context,
1015
+ variableValues,
1016
+ operationName,
1017
+ fieldResolver,
1018
+ typeResolver,
1019
+ };
1020
+ // It's important to wrap the executeFn within a promise
1021
+ // so we can easily control the end of execution (with finally)
1022
+ return Promise.resolve()
1023
+ .then(() => executeFn(executionArgs))
1024
+ .finally(destroy);
1025
+ }
1026
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1027
+ return perform(options.controller);
1028
+ }
1029
+ return contextBuilder(isNotSchema(argsOrSchema)
986
1030
  ? argsOrSchema.contextValue
987
- : contextValue);
988
- const executionArgs = isNotSchema(argsOrSchema)
989
- ? {
990
- ...argsOrSchema,
991
- contextValue: context,
992
- }
993
- : {
994
- schema: argsOrSchema,
995
- document: document,
996
- rootValue,
997
- contextValue: context,
998
- variableValues,
999
- operationName,
1000
- fieldResolver,
1001
- typeResolver,
1002
- };
1003
- // It's important to wrap the executeFn within a promise
1004
- // so we can easily control the end of execution (with finally)
1005
- return Promise.resolve()
1006
- .then(() => executeFn(executionArgs))
1007
- .finally(destroy);
1031
+ : contextValue).runWithContext(perform);
1008
1032
  };
1009
1033
  };
1010
1034
  return createExecution;
@@ -1015,43 +1039,46 @@ function subscriptionCreator({ contextBuilder, }) {
1015
1039
  // Custom or original subscribe function
1016
1040
  const subscribeFn = (options === null || options === void 0 ? void 0 : options.subscribe) || graphql.subscribe;
1017
1041
  return (argsOrSchema, document, rootValue, contextValue, variableValues, operationName, fieldResolver, subscribeFieldResolver) => {
1018
- var _a;
1019
- // Create an subscription context
1020
- const { context, ɵdestroy: destroy } = (_a = options === null || options === void 0 ? void 0 : options.controller) !== null && _a !== void 0 ? _a : contextBuilder(isNotSchema(argsOrSchema)
1042
+ function perform({ context, ɵdestroy: destroy, }) {
1043
+ const subscriptionArgs = isNotSchema(argsOrSchema)
1044
+ ? {
1045
+ ...argsOrSchema,
1046
+ contextValue: context,
1047
+ }
1048
+ : {
1049
+ schema: argsOrSchema,
1050
+ document: document,
1051
+ rootValue,
1052
+ contextValue: context,
1053
+ variableValues,
1054
+ operationName,
1055
+ fieldResolver,
1056
+ subscribeFieldResolver,
1057
+ };
1058
+ let isIterable = false;
1059
+ // It's important to wrap the subscribeFn within a promise
1060
+ // so we can easily control the end of subscription (with finally)
1061
+ return Promise.resolve()
1062
+ .then(() => subscribeFn(subscriptionArgs))
1063
+ .then((sub) => {
1064
+ if (isAsyncIterable(sub)) {
1065
+ isIterable = true;
1066
+ return tapAsyncIterator(sub, destroy);
1067
+ }
1068
+ return sub;
1069
+ })
1070
+ .finally(() => {
1071
+ if (!isIterable) {
1072
+ destroy();
1073
+ }
1074
+ });
1075
+ }
1076
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1077
+ return perform(options.controller);
1078
+ }
1079
+ return contextBuilder(isNotSchema(argsOrSchema)
1021
1080
  ? argsOrSchema.contextValue
1022
- : contextValue);
1023
- const subscriptionArgs = isNotSchema(argsOrSchema)
1024
- ? {
1025
- ...argsOrSchema,
1026
- contextValue: context,
1027
- }
1028
- : {
1029
- schema: argsOrSchema,
1030
- document: document,
1031
- rootValue,
1032
- contextValue: context,
1033
- variableValues,
1034
- operationName,
1035
- fieldResolver,
1036
- subscribeFieldResolver,
1037
- };
1038
- let isIterable = false;
1039
- // It's important to wrap the subscribeFn within a promise
1040
- // so we can easily control the end of subscription (with finally)
1041
- return Promise.resolve()
1042
- .then(() => subscribeFn(subscriptionArgs))
1043
- .then((sub) => {
1044
- if (isAsyncIterable(sub)) {
1045
- isIterable = true;
1046
- return tapAsyncIterator(sub, destroy);
1047
- }
1048
- return sub;
1049
- })
1050
- .finally(() => {
1051
- if (!isIterable) {
1052
- destroy();
1053
- }
1054
- });
1081
+ : contextValue).runWithContext(perform);
1055
1082
  };
1056
1083
  };
1057
1084
  return createSubscription;
@@ -1076,10 +1103,9 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1076
1103
  const createApolloSchema = () => {
1077
1104
  const sessions = {};
1078
1105
  const subscription = createSubscription();
1079
- function getSession(ctx) {
1106
+ function getSession(ctx, { context, ɵdestroy: destroy }) {
1080
1107
  if (!ctx[CONTEXT_ID]) {
1081
1108
  ctx[CONTEXT_ID] = uniqueId((id) => !sessions[id]);
1082
- const { context, ɵdestroy: destroy } = contextBuilder(ctx);
1083
1109
  sessions[ctx[CONTEXT_ID]] = {
1084
1110
  count: 0,
1085
1111
  session: {
@@ -1111,20 +1137,22 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1111
1137
  operationName: input.operationName,
1112
1138
  });
1113
1139
  }
1114
- // Create an execution context
1115
- const { context, destroy } = getSession(input.context);
1116
- // It's important to wrap the executeFn within a promise
1117
- // so we can easily control the end of execution (with finally)
1118
- return Promise.resolve()
1119
- .then(() => graphql.execute({
1120
- schema,
1121
- document: input.document,
1122
- contextValue: context,
1123
- variableValues: input.variables,
1124
- rootValue: input.rootValue,
1125
- operationName: input.operationName,
1126
- }))
1127
- .finally(destroy);
1140
+ // Create an execution context and run within it
1141
+ return contextBuilder(input.context).runWithContext((env) => {
1142
+ const { context, destroy } = getSession(input.context, env);
1143
+ // It's important to wrap the executeFn within a promise
1144
+ // so we can easily control the end of execution (with finally)
1145
+ return Promise.resolve()
1146
+ .then(() => graphql.execute({
1147
+ schema,
1148
+ document: input.document,
1149
+ contextValue: context,
1150
+ variableValues: input.variables,
1151
+ rootValue: input.rootValue,
1152
+ operationName: input.operationName,
1153
+ }))
1154
+ .finally(destroy);
1155
+ });
1128
1156
  },
1129
1157
  });
1130
1158
  };
package/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { makeExecutableSchema } from '@graphql-tools/schema';
2
2
  import { GraphQLSchema, execute as execute$1, subscribe, visit, Kind, concatAST, defaultFieldResolver, GraphQLScalarType, parse } from 'graphql';
3
+ import async_context from '#async-context';
3
4
  import { wrapSchema } from '@graphql-tools/wrap';
4
5
  import { mergeDeepWith } from 'ramda';
5
6
 
@@ -879,11 +880,14 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
879
880
  },
880
881
  });
881
882
  appInjector.setExecutionContextGetter(function executionContextGetter() {
882
- return appContext;
883
+ var _a;
884
+ return (((_a = async_context.getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getApplicationContext()) || appContext);
883
885
  });
884
886
  function createModuleExecutionContextGetter(moduleId) {
885
887
  return function moduleExecutionContextGetter() {
886
- return getModuleContext(moduleId, context);
888
+ var _a;
889
+ return (((_a = async_context.getAsyncContext()) === null || _a === void 0 ? void 0 : _a.getModuleContext(moduleId)) ||
890
+ getModuleContext(moduleId, context));
887
891
  };
888
892
  }
889
893
  modulesMap.forEach((mod, moduleId) => {
@@ -953,7 +957,7 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
953
957
  return getModuleContext(moduleId, sharedContext).injector;
954
958
  },
955
959
  });
956
- return {
960
+ const env = {
957
961
  ɵdestroy: once(() => {
958
962
  providersToDestroy.forEach(([injector, keyId]) => {
959
963
  // If provider was instantiated
@@ -967,6 +971,19 @@ function createContextBuilder({ appInjector, modulesMap, appLevelOperationProvid
967
971
  ɵinjector: operationAppInjector,
968
972
  context: sharedContext,
969
973
  };
974
+ return {
975
+ ...env,
976
+ runWithContext(cb) {
977
+ return async_context.runWithAsyncContext({
978
+ getApplicationContext() {
979
+ return appContext;
980
+ },
981
+ getModuleContext(moduleId) {
982
+ return getModuleContext(moduleId, context);
983
+ },
984
+ }, cb, env);
985
+ },
986
+ };
970
987
  };
971
988
  return contextBuilder;
972
989
  }
@@ -976,31 +993,34 @@ function executionCreator({ contextBuilder, }) {
976
993
  // Custom or original execute function
977
994
  const executeFn = (options === null || options === void 0 ? void 0 : options.execute) || execute$1;
978
995
  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)
996
+ function perform({ context, ɵdestroy: destroy, }) {
997
+ const executionArgs = isNotSchema(argsOrSchema)
998
+ ? {
999
+ ...argsOrSchema,
1000
+ contextValue: context,
1001
+ }
1002
+ : {
1003
+ schema: argsOrSchema,
1004
+ document: document,
1005
+ rootValue,
1006
+ contextValue: context,
1007
+ variableValues,
1008
+ operationName,
1009
+ fieldResolver,
1010
+ typeResolver,
1011
+ };
1012
+ // It's important to wrap the executeFn within a promise
1013
+ // so we can easily control the end of execution (with finally)
1014
+ return Promise.resolve()
1015
+ .then(() => executeFn(executionArgs))
1016
+ .finally(destroy);
1017
+ }
1018
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1019
+ return perform(options.controller);
1020
+ }
1021
+ return contextBuilder(isNotSchema(argsOrSchema)
982
1022
  ? 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);
1023
+ : contextValue).runWithContext(perform);
1004
1024
  };
1005
1025
  };
1006
1026
  return createExecution;
@@ -1011,43 +1031,46 @@ function subscriptionCreator({ contextBuilder, }) {
1011
1031
  // Custom or original subscribe function
1012
1032
  const subscribeFn = (options === null || options === void 0 ? void 0 : options.subscribe) || subscribe;
1013
1033
  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)
1034
+ function perform({ context, ɵdestroy: destroy, }) {
1035
+ const subscriptionArgs = isNotSchema(argsOrSchema)
1036
+ ? {
1037
+ ...argsOrSchema,
1038
+ contextValue: context,
1039
+ }
1040
+ : {
1041
+ schema: argsOrSchema,
1042
+ document: document,
1043
+ rootValue,
1044
+ contextValue: context,
1045
+ variableValues,
1046
+ operationName,
1047
+ fieldResolver,
1048
+ subscribeFieldResolver,
1049
+ };
1050
+ let isIterable = false;
1051
+ // It's important to wrap the subscribeFn within a promise
1052
+ // so we can easily control the end of subscription (with finally)
1053
+ return Promise.resolve()
1054
+ .then(() => subscribeFn(subscriptionArgs))
1055
+ .then((sub) => {
1056
+ if (isAsyncIterable(sub)) {
1057
+ isIterable = true;
1058
+ return tapAsyncIterator(sub, destroy);
1059
+ }
1060
+ return sub;
1061
+ })
1062
+ .finally(() => {
1063
+ if (!isIterable) {
1064
+ destroy();
1065
+ }
1066
+ });
1067
+ }
1068
+ if (options === null || options === void 0 ? void 0 : options.controller) {
1069
+ return perform(options.controller);
1070
+ }
1071
+ return contextBuilder(isNotSchema(argsOrSchema)
1017
1072
  ? 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
- });
1073
+ : contextValue).runWithContext(perform);
1051
1074
  };
1052
1075
  };
1053
1076
  return createSubscription;
@@ -1072,10 +1095,9 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1072
1095
  const createApolloSchema = () => {
1073
1096
  const sessions = {};
1074
1097
  const subscription = createSubscription();
1075
- function getSession(ctx) {
1098
+ function getSession(ctx, { context, ɵdestroy: destroy }) {
1076
1099
  if (!ctx[CONTEXT_ID]) {
1077
1100
  ctx[CONTEXT_ID] = uniqueId((id) => !sessions[id]);
1078
- const { context, ɵdestroy: destroy } = contextBuilder(ctx);
1079
1101
  sessions[ctx[CONTEXT_ID]] = {
1080
1102
  count: 0,
1081
1103
  session: {
@@ -1107,20 +1129,22 @@ function apolloSchemaCreator({ createSubscription, contextBuilder, schema, }) {
1107
1129
  operationName: input.operationName,
1108
1130
  });
1109
1131
  }
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);
1132
+ // Create an execution context and run within it
1133
+ return contextBuilder(input.context).runWithContext((env) => {
1134
+ const { context, destroy } = getSession(input.context, env);
1135
+ // It's important to wrap the executeFn within a promise
1136
+ // so we can easily control the end of execution (with finally)
1137
+ return Promise.resolve()
1138
+ .then(() => execute$1({
1139
+ schema,
1140
+ document: input.document,
1141
+ contextValue: context,
1142
+ variableValues: input.variables,
1143
+ rootValue: input.rootValue,
1144
+ operationName: input.operationName,
1145
+ }))
1146
+ .finally(destroy);
1147
+ });
1124
1148
  },
1125
1149
  });
1126
1150
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-modules",
3
- "version": "3.1.0",
3
+ "version": "3.1.1",
4
4
  "description": "Create reusable, maintainable, testable and extendable GraphQL modules",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
@@ -37,5 +37,11 @@
37
37
  "import": "./*.mjs"
38
38
  },
39
39
  "./package.json": "./package.json"
40
+ },
41
+ "imports": {
42
+ "#async-context": {
43
+ "node": "./async-context.node.cjs",
44
+ "default": "./async-context.browser.mjs"
45
+ }
40
46
  }
41
47
  }