relay-runtime 8.0.0 → 10.0.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.
Files changed (135) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +41 -0
  2. package/handlers/connection/ConnectionHandler.js.flow +549 -0
  3. package/handlers/connection/ConnectionInterface.js.flow +92 -0
  4. package/handlers/connection/MutationHandlers.js.flow +88 -0
  5. package/index.js +1 -1
  6. package/index.js.flow +320 -0
  7. package/lib/handlers/RelayDefaultHandlerProvider.js +13 -2
  8. package/lib/handlers/connection/{RelayConnectionHandler.js → ConnectionHandler.js} +33 -35
  9. package/lib/handlers/connection/{RelayConnectionInterface.js → ConnectionInterface.js} +2 -2
  10. package/lib/handlers/connection/MutationHandlers.js +86 -0
  11. package/lib/index.js +15 -19
  12. package/lib/mutations/RelayDeclarativeMutationConfig.js +29 -52
  13. package/lib/mutations/RelayRecordProxy.js +1 -3
  14. package/lib/mutations/RelayRecordSourceMutator.js +2 -9
  15. package/lib/mutations/RelayRecordSourceProxy.js +2 -4
  16. package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -13
  17. package/lib/mutations/commitMutation.js +13 -3
  18. package/lib/mutations/validateMutation.js +16 -9
  19. package/lib/network/RelayObservable.js +9 -9
  20. package/lib/network/RelayQueryResponseCache.js +8 -6
  21. package/lib/query/PreloadableQueryRegistry.js +70 -0
  22. package/lib/query/fetchQueryInternal.js +31 -23
  23. package/lib/store/DataChecker.js +122 -110
  24. package/lib/store/RelayConcreteVariables.js +6 -2
  25. package/lib/store/RelayModernEnvironment.js +121 -67
  26. package/lib/store/RelayModernFragmentSpecResolver.js +12 -16
  27. package/lib/store/RelayModernQueryExecutor.js +389 -314
  28. package/lib/store/RelayModernRecord.js +14 -9
  29. package/lib/store/RelayModernSelector.js +7 -3
  30. package/lib/store/RelayModernStore.js +289 -484
  31. package/lib/store/RelayOperationTracker.js +35 -78
  32. package/lib/store/RelayOptimisticRecordSource.js +7 -5
  33. package/lib/store/RelayPublishQueue.js +6 -33
  34. package/lib/store/RelayReader.js +113 -45
  35. package/lib/store/RelayRecordSource.js +2 -9
  36. package/lib/store/RelayRecordSourceMapImpl.js +13 -18
  37. package/lib/store/RelayReferenceMarker.js +40 -60
  38. package/lib/store/RelayResponseNormalizer.js +158 -193
  39. package/lib/store/RelayStoreUtils.js +1 -0
  40. package/lib/store/StoreInspector.js +8 -8
  41. package/lib/store/TypeID.js +28 -0
  42. package/lib/store/cloneRelayScalarHandleSourceField.js +44 -0
  43. package/lib/store/normalizeRelayPayload.js +6 -2
  44. package/lib/store/readInlineData.js +1 -1
  45. package/lib/subscription/requestSubscription.js +5 -3
  46. package/lib/util/RelayConcreteNode.js +9 -6
  47. package/lib/util/RelayError.js +39 -9
  48. package/lib/util/RelayFeatureFlags.js +2 -5
  49. package/lib/util/RelayReplaySubject.js +3 -3
  50. package/lib/util/createPayloadFor3DField.js +7 -2
  51. package/lib/util/getRequestIdentifier.js +2 -2
  52. package/lib/util/recycleNodesInto.js +2 -6
  53. package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
  54. package/mutations/RelayRecordProxy.js.flow +165 -0
  55. package/mutations/RelayRecordSourceMutator.js.flow +238 -0
  56. package/mutations/RelayRecordSourceProxy.js.flow +164 -0
  57. package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
  58. package/mutations/applyOptimisticMutation.js.flow +76 -0
  59. package/mutations/commitLocalUpdate.js.flow +24 -0
  60. package/mutations/commitMutation.js.flow +182 -0
  61. package/mutations/validateMutation.js.flow +213 -0
  62. package/network/ConvertToExecuteFunction.js.flow +49 -0
  63. package/network/RelayNetwork.js.flow +84 -0
  64. package/network/RelayNetworkTypes.js.flow +123 -0
  65. package/network/RelayObservable.js.flow +634 -0
  66. package/network/RelayQueryResponseCache.js.flow +111 -0
  67. package/package.json +1 -1
  68. package/query/GraphQLTag.js.flow +166 -0
  69. package/query/PreloadableQueryRegistry.js.flow +65 -0
  70. package/query/fetchQuery.js.flow +47 -0
  71. package/query/fetchQueryInternal.js.flow +348 -0
  72. package/relay-runtime.js +2 -2
  73. package/relay-runtime.min.js +2 -2
  74. package/store/ClientID.js.flow +43 -0
  75. package/store/DataChecker.js.flow +502 -0
  76. package/store/RelayConcreteVariables.js.flow +96 -0
  77. package/store/RelayModernEnvironment.js.flow +551 -0
  78. package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
  79. package/store/RelayModernOperationDescriptor.js.flow +88 -0
  80. package/store/RelayModernQueryExecutor.js.flow +1321 -0
  81. package/store/RelayModernRecord.js.flow +403 -0
  82. package/store/RelayModernSelector.js.flow +455 -0
  83. package/store/RelayModernStore.js.flow +842 -0
  84. package/store/RelayOperationTracker.js.flow +164 -0
  85. package/store/RelayOptimisticRecordSource.js.flow +119 -0
  86. package/store/RelayPublishQueue.js.flow +401 -0
  87. package/store/RelayReader.js.flow +473 -0
  88. package/store/RelayRecordSource.js.flow +29 -0
  89. package/store/RelayRecordSourceMapImpl.js.flow +87 -0
  90. package/store/RelayRecordState.js.flow +37 -0
  91. package/store/RelayReferenceMarker.js.flow +257 -0
  92. package/store/RelayResponseNormalizer.js.flow +680 -0
  93. package/store/RelayStoreTypes.js.flow +899 -0
  94. package/store/RelayStoreUtils.js.flow +219 -0
  95. package/store/StoreInspector.js.flow +171 -0
  96. package/store/TypeID.js.flow +28 -0
  97. package/store/ViewerPattern.js.flow +26 -0
  98. package/store/cloneRelayHandleSourceField.js.flow +66 -0
  99. package/store/cloneRelayScalarHandleSourceField.js.flow +62 -0
  100. package/store/createFragmentSpecResolver.js.flow +55 -0
  101. package/store/createRelayContext.js.flow +44 -0
  102. package/store/defaultGetDataID.js.flow +27 -0
  103. package/store/hasOverlappingIDs.js.flow +34 -0
  104. package/store/isRelayModernEnvironment.js.flow +27 -0
  105. package/store/normalizeRelayPayload.js.flow +51 -0
  106. package/store/readInlineData.js.flow +75 -0
  107. package/subscription/requestSubscription.js.flow +100 -0
  108. package/util/JSResourceTypes.flow.js.flow +20 -0
  109. package/util/NormalizationNode.js.flow +198 -0
  110. package/util/ReaderNode.js.flow +208 -0
  111. package/util/RelayConcreteNode.js.flow +93 -0
  112. package/util/RelayDefaultHandleKey.js.flow +17 -0
  113. package/util/RelayError.js.flow +62 -0
  114. package/util/RelayFeatureFlags.js.flow +30 -0
  115. package/util/RelayProfiler.js.flow +284 -0
  116. package/util/RelayReplaySubject.js.flow +135 -0
  117. package/util/RelayRuntimeTypes.js.flow +72 -0
  118. package/util/createPayloadFor3DField.js.flow +43 -0
  119. package/util/deepFreeze.js.flow +36 -0
  120. package/util/generateID.js.flow +21 -0
  121. package/util/getFragmentIdentifier.js.flow +52 -0
  122. package/util/getRelayHandleKey.js.flow +41 -0
  123. package/util/getRequestIdentifier.js.flow +42 -0
  124. package/util/isPromise.js.flow +21 -0
  125. package/util/isScalarAndEqual.js.flow +26 -0
  126. package/util/recycleNodesInto.js.flow +76 -0
  127. package/util/resolveImmediate.js.flow +30 -0
  128. package/util/stableCopy.js.flow +35 -0
  129. package/lib/handlers/RelayDefaultMissingFieldHandlers.js +0 -26
  130. package/lib/handlers/getRelayDefaultMissingFieldHandlers.js +0 -36
  131. package/lib/query/RelayModernGraphQLTag.js +0 -104
  132. package/lib/store/RelayConnection.js +0 -37
  133. package/lib/store/RelayConnectionResolver.js +0 -178
  134. package/lib/store/RelayRecordSourceObjectImpl.js +0 -79
  135. package/lib/util/getFragmentSpecIdentifier.js +0 -27
@@ -12,7 +12,11 @@
12
12
 
13
13
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
14
14
 
15
- var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
15
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
+
17
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
18
+
19
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
16
20
 
17
21
  var RelayModernRecord = require('./RelayModernRecord');
18
22
 
@@ -27,7 +31,7 @@ function normalizeRelayPayload(selector, payload, errors, options) {
27
31
  var source = RelayRecordSource.create();
28
32
  source.set(selector.dataID, RelayModernRecord.create(selector.dataID, ROOT_TYPE));
29
33
  var relayPayload = RelayResponseNormalizer.normalize(source, selector, payload, options);
30
- return (0, _objectSpread2["default"])({}, relayPayload, {
34
+ return _objectSpread({}, relayPayload, {
31
35
  errors: errors
32
36
  });
33
37
  }
@@ -27,7 +27,7 @@ function readInlineData(fragment, fragmentRef) {
27
27
  return fragmentRef;
28
28
  }
29
29
 
30
- !(typeof fragmentRef === 'object') ? process.env.NODE_ENV !== "production" ? invariant(false, 'readInlineData(): Expected an object, got `%s`.', typeof fragmentRef) : invariant(false) : void 0; // $FlowFixMe
30
+ !(typeof fragmentRef === 'object') ? process.env.NODE_ENV !== "production" ? invariant(false, 'readInlineData(): Expected an object, got `%s`.', typeof fragmentRef) : invariant(false) : void 0; // $FlowFixMe[incompatible-use]
31
31
 
32
32
  var inlineData = (_fragmentRef$FRAGMENT = fragmentRef[FRAGMENTS_KEY]) === null || _fragmentRef$FRAGMENT === void 0 ? void 0 : _fragmentRef$FRAGMENT[inlineDataFragment.name];
33
33
  !(inlineData != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'readInlineData(): Expected fragment `%s` to be spread in the parent ' + 'fragment.', inlineDataFragment.name) : invariant(false) : void 0;
@@ -31,7 +31,8 @@ function requestSubscription(environment, config) {
31
31
  onCompleted = config.onCompleted,
32
32
  onError = config.onError,
33
33
  onNext = config.onNext,
34
- variables = config.variables;
34
+ variables = config.variables,
35
+ cacheConfig = config.cacheConfig;
35
36
  var operation = createOperationDescriptor(subscription, variables);
36
37
  process.env.NODE_ENV !== "production" ? warning(!(config.updater && configs), 'requestSubscription: Expected only one of `updater` and `configs` to be provided') : void 0;
37
38
 
@@ -42,9 +43,10 @@ function requestSubscription(environment, config) {
42
43
 
43
44
  var sub = environment.execute({
44
45
  operation: operation,
45
- updater: updater
46
+ updater: updater,
47
+ cacheConfig: cacheConfig
46
48
  }).map(function () {
47
- var data = environment.lookup(operation.fragment).data; // $FlowFixMe
49
+ var data = environment.lookup(operation.fragment).data; // $FlowFixMe[incompatible-cast]
48
50
 
49
51
  return data;
50
52
  }).subscribe({
@@ -11,15 +11,17 @@
11
11
  'use strict';
12
12
 
13
13
  /**
14
- * Represents a common GraphQL request with `text` (or persisted `id`) can be
15
- * used to execute it, an `operation` containing information to normalize the
16
- * results, and a `fragment` derived from that operation to read the response
17
- * data (masking data from child fragments).
14
+ * Represents a common GraphQL request that can be executed, an `operation`
15
+ * containing information to normalize the results, and a `fragment` derived
16
+ * from that operation to read the response data (masking data from child
17
+ * fragments).
18
18
  */
19
19
 
20
20
  /**
21
- * Contains the `text` (or persisted `id`) required for executing a common
22
- * GraphQL request.
21
+ * Contains the parameters required for executing a GraphQL request.
22
+ * The operation can either be provided as a persisted `id` or `text`. If given
23
+ * in `text` format, a `cacheID` as a hash of the text should be set to be used
24
+ * for local caching.
23
25
  */
24
26
  var RelayConcreteNode = {
25
27
  CONDITION: 'Condition',
@@ -45,6 +47,7 @@ var RelayConcreteNode = {
45
47
  SCALAR_HANDLE: 'ScalarHandle',
46
48
  SPLIT_OPERATION: 'SplitOperation',
47
49
  STREAM: 'Stream',
50
+ TYPE_DISCRIMINATOR: 'TypeDiscriminator',
48
51
  VARIABLE: 'Variable'
49
52
  };
50
53
  module.exports = RelayConcreteNode;
@@ -13,19 +13,49 @@
13
13
  * @private
14
14
  */
15
15
 
16
- function createError(type, name, message) {
17
- var error = new Error(message);
18
- error.name = name;
19
- error.type = type;
20
- error.framesToPop = 2;
16
+ function createError(type, name, messageFormat) {
17
+ for (var _len = arguments.length, messageParams = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
18
+ messageParams[_key - 3] = arguments[_key];
19
+ }
20
+
21
+ var index = 0;
22
+ var message = messageFormat.replace(/%s/g, function () {
23
+ return String(messageParams[index++]);
24
+ });
25
+ var err = new Error(message);
26
+ var error = Object.assign(err, {
27
+ name: name,
28
+ messageFormat: messageFormat,
29
+ messageParams: messageParams,
30
+ type: type,
31
+ taalOpcodes: [2, 2] // skip frame (code=2) twice
32
+
33
+ }); // In V8, Error objects keep the closure scope chain alive until the
34
+ // err.stack property is accessed.
35
+
36
+ if (error.stack === undefined) {
37
+ // IE sets the stack only if error is thrown
38
+ try {
39
+ throw error;
40
+ } catch (_unused) {}
41
+ }
42
+
21
43
  return error;
22
44
  }
23
45
 
24
46
  module.exports = {
25
- create: function create(name, message) {
26
- return createError('mustfix', name, message);
47
+ create: function create(name, messageFormat) {
48
+ for (var _len2 = arguments.length, messageParams = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
49
+ messageParams[_key2 - 2] = arguments[_key2];
50
+ }
51
+
52
+ return createError.apply(void 0, ['error', name, messageFormat].concat(messageParams));
27
53
  },
28
- createWarning: function createWarning(name, message) {
29
- return createError('warn', name, message);
54
+ createWarning: function createWarning(name, messageFormat) {
55
+ for (var _len3 = arguments.length, messageParams = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {
56
+ messageParams[_key3 - 2] = arguments[_key3];
57
+ }
58
+
59
+ return createError.apply(void 0, ['warn', name, messageFormat].concat(messageParams));
30
60
  }
31
61
  };
@@ -13,11 +13,8 @@
13
13
  var RelayFeatureFlags = {
14
14
  // T45504512: new connection model
15
15
  ENABLE_VARIABLE_CONNECTION_KEY: false,
16
- ENABLE_CONNECTION_RESOLVERS: false,
17
16
  ENABLE_PARTIAL_RENDERING_DEFAULT: false,
18
- ENABLE_RELAY_CONTAINERS_SUSPENSE: true,
19
- ENABLE_MISSING_VIEWER_FIELD_HANDLER: true,
20
- ENABLE_UNIQUE_MUTATION_ROOT: true,
21
- USE_RECORD_SOURCE_MAP_IMPL: false
17
+ ENABLE_RELAY_CONTAINERS_SUSPENSE: false,
18
+ ENABLE_PRECISE_TYPE_REFINEMENT: false
22
19
  };
23
20
  module.exports = RelayFeatureFlags;
@@ -24,9 +24,7 @@ var invariant = require("fbjs/lib/invariant");
24
24
  * Records events provided and synchronously plays them back to new subscribers,
25
25
  * as well as forwarding new asynchronous events.
26
26
  */
27
- var RelayReplaySubject =
28
- /*#__PURE__*/
29
- function () {
27
+ var RelayReplaySubject = /*#__PURE__*/function () {
30
28
  function RelayReplaySubject() {
31
29
  var _this = this;
32
30
 
@@ -130,6 +128,8 @@ function () {
130
128
  _proto.unsubscribe = function unsubscribe() {
131
129
  if (this._subscription) {
132
130
  this._subscription.unsubscribe();
131
+
132
+ this._subscription = null;
133
133
  }
134
134
  };
135
135
 
@@ -13,14 +13,19 @@
13
13
 
14
14
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
15
15
 
16
- var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
16
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
17
+
18
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
19
+
20
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
17
21
 
18
22
  var _require = require('../store/RelayStoreUtils'),
19
23
  getModuleComponentKey = _require.getModuleComponentKey,
20
24
  getModuleOperationKey = _require.getModuleOperationKey;
21
25
 
22
26
  function createPayloadFor3DField(name, operation, component, response) {
23
- var data = (0, _objectSpread2["default"])({}, response);
27
+ var data = _objectSpread({}, response);
28
+
24
29
  data[getModuleComponentKey(name)] = component;
25
30
  data[getModuleOperationKey(name)] = operation;
26
31
  return data;
@@ -19,8 +19,8 @@ var stableCopy = require('./stableCopy');
19
19
  * variables.
20
20
  */
21
21
  function getRequestIdentifier(parameters, variables) {
22
- var requestID = parameters.id != null ? parameters.id : parameters.text;
23
- !(requestID != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'getRequestIdentifier: Expected request `%s` to have either a ' + 'valid `id` or `text` property', parameters.name) : invariant(false) : void 0;
22
+ var requestID = parameters.cacheID != null ? parameters.cacheID : parameters.id;
23
+ !(requestID != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'getRequestIdentifier: Expected request `%s` to have either a ' + 'valid `id` or `cacheID` property', parameters.name) : invariant(false) : void 0;
24
24
  return requestID + JSON.stringify(stableCopy(variables));
25
25
  }
26
26
 
@@ -53,15 +53,11 @@ function recycleNodesInto(prevData, nextData) {
53
53
  if (nextValue !== nextObject[key]) {
54
54
  if (process.env.NODE_ENV !== "production") {
55
55
  if (!Object.isFrozen(nextObject)) {
56
- /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This
57
- * comment suppresses an error found when Flow v0.98 was deployed.
58
- * To see the error delete this comment and run Flow. */
56
+ // $FlowFixMe[cannot-write]
59
57
  nextObject[key] = nextValue;
60
58
  }
61
59
  } else {
62
- /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
63
- * suppresses an error found when Flow v0.98 was deployed. To see
64
- * the error delete this comment and run Flow. */
60
+ // $FlowFixMe[cannot-write]
65
61
  nextObject[key] = nextValue;
66
62
  }
67
63
  }
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const ConnectionHandler = require('../handlers/connection/ConnectionHandler');
16
+
17
+ const warning = require('warning');
18
+
19
+ import type {
20
+ RecordSourceSelectorProxy,
21
+ SelectorData,
22
+ SelectorStoreUpdater,
23
+ } from '../store/RelayStoreTypes';
24
+ import type {ConcreteRequest} from '../util/RelayConcreteNode';
25
+ import type {Variables} from '../util/RelayRuntimeTypes';
26
+
27
+ const MutationTypes = Object.freeze({
28
+ RANGE_ADD: 'RANGE_ADD',
29
+ RANGE_DELETE: 'RANGE_DELETE',
30
+ NODE_DELETE: 'NODE_DELETE',
31
+ });
32
+ export type MutationType = $Values<typeof MutationTypes>;
33
+
34
+ const RangeOperations = Object.freeze({
35
+ APPEND: 'append',
36
+ PREPEND: 'prepend',
37
+ });
38
+ export type RangeOperation = $Values<typeof RangeOperations>;
39
+
40
+ type RangeBehaviorsFunction = (connectionArgs: {
41
+ [name: string]: $FlowFixMe,
42
+ ...,
43
+ }) => RangeOperation;
44
+ type RangeBehaviorsObject = {[key: string]: RangeOperation, ...};
45
+ export type RangeBehaviors = RangeBehaviorsFunction | RangeBehaviorsObject;
46
+
47
+ type RangeAddConfig = {|
48
+ type: 'RANGE_ADD',
49
+ parentName?: string,
50
+ parentID?: string,
51
+ connectionInfo?: Array<{|
52
+ key: string,
53
+ filters?: Variables,
54
+ rangeBehavior: string,
55
+ |}>,
56
+ connectionName?: string,
57
+ edgeName: string,
58
+ rangeBehaviors?: RangeBehaviors,
59
+ |};
60
+
61
+ type RangeDeleteConfig = {|
62
+ type: 'RANGE_DELETE',
63
+ parentName?: string,
64
+ parentID?: string,
65
+ connectionKeys?: Array<{|
66
+ key: string,
67
+ filters?: Variables,
68
+ |}>,
69
+ connectionName?: string,
70
+ deletedIDFieldName: string | Array<string>,
71
+ pathToConnection: Array<string>,
72
+ |};
73
+
74
+ type NodeDeleteConfig = {|
75
+ type: 'NODE_DELETE',
76
+ parentName?: string,
77
+ parentID?: string,
78
+ connectionName?: string,
79
+ deletedIDFieldName: string,
80
+ |};
81
+
82
+ export type DeclarativeMutationConfig =
83
+ | RangeAddConfig
84
+ | RangeDeleteConfig
85
+ | NodeDeleteConfig;
86
+
87
+ function convert(
88
+ configs: Array<DeclarativeMutationConfig>,
89
+ request: ConcreteRequest,
90
+ optimisticUpdater?: ?SelectorStoreUpdater,
91
+ updater?: ?SelectorStoreUpdater,
92
+ ): {
93
+ optimisticUpdater: SelectorStoreUpdater,
94
+ updater: SelectorStoreUpdater,
95
+ ...
96
+ } {
97
+ const configOptimisticUpdates = optimisticUpdater ? [optimisticUpdater] : [];
98
+ const configUpdates = updater ? [updater] : [];
99
+ configs.forEach(config => {
100
+ switch (config.type) {
101
+ case 'NODE_DELETE':
102
+ const nodeDeleteResult = nodeDelete(config, request);
103
+ if (nodeDeleteResult) {
104
+ configOptimisticUpdates.push(nodeDeleteResult);
105
+ configUpdates.push(nodeDeleteResult);
106
+ }
107
+ break;
108
+ case 'RANGE_ADD':
109
+ const rangeAddResult = rangeAdd(config, request);
110
+ if (rangeAddResult) {
111
+ configOptimisticUpdates.push(rangeAddResult);
112
+ configUpdates.push(rangeAddResult);
113
+ }
114
+ break;
115
+ case 'RANGE_DELETE':
116
+ const rangeDeleteResult = rangeDelete(config, request);
117
+ if (rangeDeleteResult) {
118
+ configOptimisticUpdates.push(rangeDeleteResult);
119
+ configUpdates.push(rangeDeleteResult);
120
+ }
121
+ break;
122
+ }
123
+ });
124
+ return {
125
+ optimisticUpdater: (
126
+ store: RecordSourceSelectorProxy,
127
+ data: ?SelectorData,
128
+ ) => {
129
+ configOptimisticUpdates.forEach(eachOptimisticUpdater => {
130
+ eachOptimisticUpdater(store, data);
131
+ });
132
+ },
133
+ updater: (store: RecordSourceSelectorProxy, data: ?SelectorData) => {
134
+ configUpdates.forEach(eachUpdater => {
135
+ eachUpdater(store, data);
136
+ });
137
+ },
138
+ };
139
+ }
140
+
141
+ function nodeDelete(
142
+ config: NodeDeleteConfig,
143
+ request: ConcreteRequest,
144
+ ): ?SelectorStoreUpdater {
145
+ const {deletedIDFieldName} = config;
146
+ const rootField = getRootField(request);
147
+ if (!rootField) {
148
+ return null;
149
+ }
150
+ return (store: RecordSourceSelectorProxy, data: ?SelectorData) => {
151
+ const payload = store.getRootField(rootField);
152
+ if (!payload) {
153
+ return;
154
+ }
155
+ const deleteID = payload.getValue(deletedIDFieldName);
156
+ const deleteIDs = Array.isArray(deleteID) ? deleteID : [deleteID];
157
+ deleteIDs.forEach(id => {
158
+ if (id && typeof id === 'string') {
159
+ store.delete(id);
160
+ }
161
+ });
162
+ };
163
+ }
164
+
165
+ function rangeAdd(
166
+ config: RangeAddConfig,
167
+ request: ConcreteRequest,
168
+ ): ?SelectorStoreUpdater {
169
+ const {parentID, connectionInfo, edgeName} = config;
170
+ if (!parentID) {
171
+ warning(
172
+ false,
173
+ 'RelayDeclarativeMutationConfig: For mutation config RANGE_ADD ' +
174
+ 'to work you must include a parentID',
175
+ );
176
+ return null;
177
+ }
178
+ const rootField = getRootField(request);
179
+ if (!connectionInfo || !rootField) {
180
+ return null;
181
+ }
182
+ return (store: RecordSourceSelectorProxy, data: ?SelectorData) => {
183
+ const parent = store.get(parentID);
184
+ if (!parent) {
185
+ return;
186
+ }
187
+ const payload = store.getRootField(rootField);
188
+ if (!payload) {
189
+ return;
190
+ }
191
+ const serverEdge = payload.getLinkedRecord(edgeName);
192
+ for (const info of connectionInfo) {
193
+ if (!serverEdge) {
194
+ continue;
195
+ }
196
+ const connection = ConnectionHandler.getConnection(
197
+ parent,
198
+ info.key,
199
+ info.filters,
200
+ );
201
+ if (!connection) {
202
+ continue;
203
+ }
204
+ const clientEdge = ConnectionHandler.buildConnectionEdge(
205
+ store,
206
+ connection,
207
+ serverEdge,
208
+ );
209
+ if (!clientEdge) {
210
+ continue;
211
+ }
212
+ switch (info.rangeBehavior) {
213
+ case 'append':
214
+ ConnectionHandler.insertEdgeAfter(connection, clientEdge);
215
+ break;
216
+ case 'prepend':
217
+ ConnectionHandler.insertEdgeBefore(connection, clientEdge);
218
+ break;
219
+ default:
220
+ warning(
221
+ false,
222
+ 'RelayDeclarativeMutationConfig: RANGE_ADD range behavior `%s` ' +
223
+ 'will not work as expected in RelayModern, supported range ' +
224
+ "behaviors are 'append', 'prepend'.",
225
+ info.rangeBehavior,
226
+ );
227
+ break;
228
+ }
229
+ }
230
+ };
231
+ }
232
+
233
+ function rangeDelete(
234
+ config: RangeDeleteConfig,
235
+ request: ConcreteRequest,
236
+ ): ?SelectorStoreUpdater {
237
+ const {
238
+ parentID,
239
+ connectionKeys,
240
+ pathToConnection,
241
+ deletedIDFieldName,
242
+ } = config;
243
+ if (!parentID) {
244
+ warning(
245
+ false,
246
+ 'RelayDeclarativeMutationConfig: For mutation config RANGE_DELETE ' +
247
+ 'to work you must include a parentID',
248
+ );
249
+ return null;
250
+ }
251
+ const rootField = getRootField(request);
252
+ if (!rootField) {
253
+ return null;
254
+ }
255
+ return (store: RecordSourceSelectorProxy, data: ?SelectorData) => {
256
+ if (!data) {
257
+ return;
258
+ }
259
+ const deleteIDs = [];
260
+ let deletedIDField = data[rootField];
261
+ if (deletedIDField && Array.isArray(deletedIDFieldName)) {
262
+ for (const eachField of deletedIDFieldName) {
263
+ if (deletedIDField && typeof deletedIDField === 'object') {
264
+ deletedIDField = deletedIDField[eachField];
265
+ }
266
+ }
267
+ if (Array.isArray(deletedIDField)) {
268
+ deletedIDField.forEach(idObject => {
269
+ if (
270
+ idObject &&
271
+ idObject.id &&
272
+ typeof idObject === 'object' &&
273
+ typeof idObject.id === 'string'
274
+ ) {
275
+ deleteIDs.push(idObject.id);
276
+ }
277
+ });
278
+ } else if (
279
+ deletedIDField &&
280
+ deletedIDField.id &&
281
+ typeof deletedIDField.id === 'string'
282
+ ) {
283
+ deleteIDs.push(deletedIDField.id);
284
+ }
285
+ } else if (
286
+ deletedIDField &&
287
+ typeof deletedIDFieldName === 'string' &&
288
+ typeof deletedIDField === 'object'
289
+ ) {
290
+ deletedIDField = deletedIDField[deletedIDFieldName];
291
+ if (typeof deletedIDField === 'string') {
292
+ deleteIDs.push(deletedIDField);
293
+ } else if (Array.isArray(deletedIDField)) {
294
+ deletedIDField.forEach(id => {
295
+ if (typeof id === 'string') {
296
+ deleteIDs.push(id);
297
+ }
298
+ });
299
+ }
300
+ }
301
+ deleteNode(parentID, connectionKeys, pathToConnection, store, deleteIDs);
302
+ };
303
+ }
304
+
305
+ function deleteNode(
306
+ parentID: string,
307
+ connectionKeys: ?Array<{|
308
+ key: string,
309
+ filters?: Variables,
310
+ |}>,
311
+ pathToConnection: Array<string>,
312
+ store: RecordSourceSelectorProxy,
313
+ deleteIDs: Array<string>,
314
+ ): void {
315
+ warning(
316
+ connectionKeys != null,
317
+ 'RelayDeclarativeMutationConfig: RANGE_DELETE must provide a ' +
318
+ 'connectionKeys',
319
+ );
320
+ const parent = store.get(parentID);
321
+ if (!parent) {
322
+ return;
323
+ }
324
+ if (pathToConnection.length < 2) {
325
+ warning(
326
+ false,
327
+ 'RelayDeclarativeMutationConfig: RANGE_DELETE ' +
328
+ 'pathToConnection must include at least parent and connection',
329
+ );
330
+ return;
331
+ }
332
+ let recordProxy = parent;
333
+ for (let i = 1; i < pathToConnection.length - 1; i++) {
334
+ if (recordProxy) {
335
+ recordProxy = recordProxy.getLinkedRecord(pathToConnection[i]);
336
+ }
337
+ }
338
+ // Should never enter loop except edge cases
339
+ if (!connectionKeys || !recordProxy) {
340
+ warning(
341
+ false,
342
+ 'RelayDeclarativeMutationConfig: RANGE_DELETE ' +
343
+ 'pathToConnection is incorrect. Unable to find connection with ' +
344
+ 'parentID: %s and path: %s',
345
+ parentID,
346
+ pathToConnection.toString(),
347
+ );
348
+ return;
349
+ }
350
+ for (const key of connectionKeys) {
351
+ const connection = ConnectionHandler.getConnection(
352
+ recordProxy,
353
+ key.key,
354
+ key.filters,
355
+ );
356
+ if (connection) {
357
+ deleteIDs.forEach(deleteID => {
358
+ ConnectionHandler.deleteNode(connection, deleteID);
359
+ });
360
+ }
361
+ }
362
+ }
363
+
364
+ function getRootField(request: ConcreteRequest): ?string {
365
+ if (
366
+ request.fragment.selections &&
367
+ request.fragment.selections.length > 0 &&
368
+ request.fragment.selections[0].kind === 'LinkedField'
369
+ ) {
370
+ return request.fragment.selections[0].name;
371
+ }
372
+ return null;
373
+ }
374
+
375
+ module.exports = {
376
+ MutationTypes,
377
+ RangeOperations,
378
+
379
+ convert,
380
+ };