relay-runtime 10.0.1 → 10.1.3

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 (82) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +6 -0
  2. package/handlers/connection/MutationHandlers.js.flow +152 -20
  3. package/index.js +1 -1
  4. package/index.js.flow +17 -1
  5. package/lib/handlers/RelayDefaultHandlerProvider.js +9 -0
  6. package/lib/handlers/connection/MutationHandlers.js +185 -21
  7. package/lib/index.js +7 -0
  8. package/lib/mutations/RelayDeclarativeMutationConfig.js +5 -7
  9. package/lib/mutations/commitMutation.js +1 -4
  10. package/lib/mutations/validateMutation.js +28 -12
  11. package/lib/network/RelayQueryResponseCache.js +3 -7
  12. package/lib/query/GraphQLTag.js +2 -1
  13. package/lib/query/fetchQuery.js +2 -3
  14. package/lib/query/fetchQueryInternal.js +2 -3
  15. package/lib/store/DataChecker.js +85 -10
  16. package/lib/store/RelayConcreteVariables.js +2 -6
  17. package/lib/store/RelayModernEnvironment.js +81 -72
  18. package/lib/store/RelayModernFragmentSpecResolver.js +14 -7
  19. package/lib/store/RelayModernOperationDescriptor.js +6 -5
  20. package/lib/store/RelayModernQueryExecutor.js +46 -33
  21. package/lib/store/RelayModernRecord.js +3 -7
  22. package/lib/store/RelayModernStore.js +39 -137
  23. package/lib/store/RelayOperationTracker.js +7 -9
  24. package/lib/store/RelayOptimisticRecordSource.js +2 -6
  25. package/lib/store/RelayPublishQueue.js +1 -1
  26. package/lib/store/RelayReader.js +196 -33
  27. package/lib/store/RelayRecordSourceMapImpl.js +3 -5
  28. package/lib/store/RelayReferenceMarker.js +87 -5
  29. package/lib/store/RelayResponseNormalizer.js +115 -19
  30. package/lib/store/RelayStoreReactFlightUtils.js +47 -0
  31. package/lib/store/RelayStoreSubscriptions.js +162 -0
  32. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +258 -0
  33. package/lib/store/StoreInspector.js +2 -6
  34. package/lib/store/createRelayContext.js +5 -0
  35. package/lib/store/defaultRequiredFieldLogger.js +18 -0
  36. package/lib/store/normalizeRelayPayload.js +2 -6
  37. package/lib/subscription/requestSubscription.js +2 -3
  38. package/lib/util/NormalizationNode.js +1 -5
  39. package/lib/util/RelayConcreteNode.js +2 -0
  40. package/lib/util/RelayFeatureFlags.js +7 -2
  41. package/lib/util/createPayloadFor3DField.js +2 -7
  42. package/lib/util/getFragmentIdentifier.js +12 -3
  43. package/lib/util/getOperation.js +33 -0
  44. package/lib/util/isEmptyObject.js +25 -0
  45. package/lib/util/recycleNodesInto.js +4 -1
  46. package/lib/util/reportMissingRequiredFields.js +48 -0
  47. package/mutations/commitMutation.js.flow +1 -2
  48. package/mutations/validateMutation.js.flow +34 -5
  49. package/network/RelayNetworkTypes.js.flow +22 -0
  50. package/package.json +2 -2
  51. package/query/GraphQLTag.js.flow +3 -1
  52. package/query/fetchQuery.js.flow +2 -2
  53. package/query/fetchQueryInternal.js.flow +0 -5
  54. package/relay-runtime.js +2 -2
  55. package/relay-runtime.min.js +2 -2
  56. package/store/DataChecker.js.flow +68 -2
  57. package/store/RelayModernEnvironment.js.flow +107 -87
  58. package/store/RelayModernFragmentSpecResolver.js.flow +13 -1
  59. package/store/RelayModernOperationDescriptor.js.flow +5 -1
  60. package/store/RelayModernQueryExecutor.js.flow +47 -23
  61. package/store/RelayModernStore.js.flow +33 -107
  62. package/store/RelayPublishQueue.js.flow +1 -1
  63. package/store/RelayReader.js.flow +180 -15
  64. package/store/RelayReferenceMarker.js.flow +72 -5
  65. package/store/RelayResponseNormalizer.js.flow +130 -19
  66. package/store/RelayStoreReactFlightUtils.js.flow +64 -0
  67. package/store/RelayStoreSubscriptions.js.flow +168 -0
  68. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +259 -0
  69. package/store/RelayStoreTypes.js.flow +130 -37
  70. package/store/createRelayContext.js.flow +3 -0
  71. package/store/defaultRequiredFieldLogger.js.flow +23 -0
  72. package/subscription/requestSubscription.js.flow +5 -2
  73. package/util/NormalizationNode.js.flow +17 -2
  74. package/util/ReaderNode.js.flow +20 -1
  75. package/util/RelayConcreteNode.js.flow +6 -0
  76. package/util/RelayFeatureFlags.js.flow +12 -1
  77. package/util/getFragmentIdentifier.js.flow +33 -9
  78. package/util/getOperation.js.flow +40 -0
  79. package/util/getRequestIdentifier.js.flow +1 -1
  80. package/util/isEmptyObject.js.flow +25 -0
  81. package/util/recycleNodesInto.js.flow +11 -0
  82. package/util/reportMissingRequiredFields.js.flow +51 -0
@@ -12,6 +12,8 @@
12
12
 
13
13
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
14
14
 
15
+ var _createForOfIteratorHelper2 = _interopRequireDefault(require("@babel/runtime/helpers/createForOfIteratorHelper"));
16
+
15
17
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
16
18
 
17
19
  var RelayFeatureFlags = require('../util/RelayFeatureFlags');
@@ -20,6 +22,8 @@ var RelayModernRecord = require('./RelayModernRecord');
20
22
 
21
23
  var RelayProfiler = require('../util/RelayProfiler');
22
24
 
25
+ var areEqual = require("fbjs/lib/areEqual");
26
+
23
27
  var invariant = require("fbjs/lib/invariant");
24
28
 
25
29
  var warning = require("fbjs/lib/warning");
@@ -28,6 +32,7 @@ var _require = require('../util/RelayConcreteNode'),
28
32
  CONDITION = _require.CONDITION,
29
33
  CLIENT_EXTENSION = _require.CLIENT_EXTENSION,
30
34
  DEFER = _require.DEFER,
35
+ FLIGHT_FIELD = _require.FLIGHT_FIELD,
31
36
  INLINE_FRAGMENT = _require.INLINE_FRAGMENT,
32
37
  LINKED_FIELD = _require.LINKED_FIELD,
33
38
  LINKED_HANDLE = _require.LINKED_HANDLE,
@@ -44,18 +49,25 @@ var _require2 = require('./ClientID'),
44
49
  var _require3 = require('./RelayModernSelector'),
45
50
  createNormalizationSelector = _require3.createNormalizationSelector;
46
51
 
47
- var _require4 = require('./RelayStoreUtils'),
48
- getArgumentValues = _require4.getArgumentValues,
49
- getHandleStorageKey = _require4.getHandleStorageKey,
50
- getModuleComponentKey = _require4.getModuleComponentKey,
51
- getModuleOperationKey = _require4.getModuleOperationKey,
52
- getStorageKey = _require4.getStorageKey,
53
- TYPENAME_KEY = _require4.TYPENAME_KEY,
54
- ROOT_ID = _require4.ROOT_ID;
55
-
56
- var _require5 = require('./TypeID'),
57
- generateTypeID = _require5.generateTypeID,
58
- TYPE_SCHEMA_TYPE = _require5.TYPE_SCHEMA_TYPE;
52
+ var _require4 = require('./RelayStoreReactFlightUtils'),
53
+ refineToReactFlightPayloadData = _require4.refineToReactFlightPayloadData,
54
+ REACT_FLIGHT_QUERIES_STORAGE_KEY = _require4.REACT_FLIGHT_QUERIES_STORAGE_KEY,
55
+ REACT_FLIGHT_TREE_STORAGE_KEY = _require4.REACT_FLIGHT_TREE_STORAGE_KEY,
56
+ REACT_FLIGHT_TYPE_NAME = _require4.REACT_FLIGHT_TYPE_NAME;
57
+
58
+ var _require5 = require('./RelayStoreUtils'),
59
+ getArgumentValues = _require5.getArgumentValues,
60
+ getHandleStorageKey = _require5.getHandleStorageKey,
61
+ getModuleComponentKey = _require5.getModuleComponentKey,
62
+ getModuleOperationKey = _require5.getModuleOperationKey,
63
+ getStorageKey = _require5.getStorageKey,
64
+ TYPENAME_KEY = _require5.TYPENAME_KEY,
65
+ ROOT_ID = _require5.ROOT_ID,
66
+ ROOT_TYPE = _require5.ROOT_TYPE;
67
+
68
+ var _require6 = require('./TypeID'),
69
+ generateTypeID = _require6.generateTypeID,
70
+ TYPE_SCHEMA_TYPE = _require6.TYPE_SCHEMA_TYPE;
59
71
 
60
72
  /**
61
73
  * Normalizes the results of a query and standard GraphQL response, writing the
@@ -87,6 +99,7 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
87
99
  this._path = options.path ? (0, _toConsumableArray2["default"])(options.path) : [];
88
100
  this._recordSource = recordSource;
89
101
  this._variables = variables;
102
+ this._reactFlightPayloadDeserializer = options.reactFlightPayloadDeserializer;
90
103
  }
91
104
 
92
105
  var _proto = RelayResponseNormalizer.prototype;
@@ -252,6 +265,15 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
252
265
  this._isClientExtension = isClientExtension;
253
266
  break;
254
267
 
268
+ case FLIGHT_FIELD:
269
+ if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
270
+ this._normalizeFlightField(node, selection, record, data);
271
+ } else {
272
+ throw new Error('Flight fields are not yet supported.');
273
+ }
274
+
275
+ break;
276
+
255
277
  default:
256
278
  selection;
257
279
  !false ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayResponseNormalizer(): Unexpected ast kind `%s`.', selection.kind) : invariant(false) : void 0;
@@ -366,8 +388,10 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
366
388
  }
367
389
  }
368
390
 
369
- if (selection.kind === SCALAR_FIELD && process.env.NODE_ENV !== "production") {
370
- this._validateConflictingFieldsWithIdenticalId(record, storageKey, fieldValue);
391
+ if (process.env.NODE_ENV !== "production") {
392
+ if (selection.kind === SCALAR_FIELD) {
393
+ this._validateConflictingFieldsWithIdenticalId(record, storageKey, fieldValue);
394
+ }
371
395
  }
372
396
 
373
397
  RelayModernRecord.setValue(record, storageKey, null);
@@ -375,7 +399,9 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
375
399
  }
376
400
 
377
401
  if (selection.kind === SCALAR_FIELD) {
378
- this._validateConflictingFieldsWithIdenticalId(record, storageKey, fieldValue);
402
+ if (process.env.NODE_ENV !== "production") {
403
+ this._validateConflictingFieldsWithIdenticalId(record, storageKey, fieldValue);
404
+ }
379
405
 
380
406
  RelayModernRecord.setValue(record, storageKey, fieldValue);
381
407
  } else if (selection.kind === LINKED_FIELD) {
@@ -394,6 +420,70 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
394
420
  }
395
421
  };
396
422
 
423
+ _proto._normalizeFlightField = function _normalizeFlightField(parent, selection, record, data) {
424
+ var responseKey = selection.alias || selection.name;
425
+ var storageKey = getStorageKey(selection, this._variables);
426
+ var fieldValue = data[responseKey];
427
+
428
+ if (fieldValue == null) {
429
+ RelayModernRecord.setValue(record, storageKey, null);
430
+ return;
431
+ }
432
+
433
+ var reactFlightPayload = refineToReactFlightPayloadData(fieldValue);
434
+ !(reactFlightPayload != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayResponseNormalizer(): Expected React Flight payload data ' + 'to be an object with `tree` and `queries` properties, got `%s`.', fieldValue) : invariant(false) : void 0;
435
+ !(typeof this._reactFlightPayloadDeserializer === 'function') ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayResponseNormalizer: Expected reactFlightPayloadDeserializer to ' + 'be a function, got `%s`.', this._reactFlightPayloadDeserializer) : invariant(false) : void 0; // We store the deserialized reactFlightClientResponse in a separate
436
+ // record and link it to the parent record. This is so we can GC the Flight
437
+ // tree later even if the parent record is still reachable.
438
+
439
+ var reactFlightClientResponse = this._reactFlightPayloadDeserializer(reactFlightPayload.tree);
440
+
441
+ var reactFlightID = generateClientID(RelayModernRecord.getDataID(record), getStorageKey(selection, this._variables));
442
+
443
+ var reactFlightClientResponseRecord = this._recordSource.get(reactFlightID);
444
+
445
+ if (reactFlightClientResponseRecord == null) {
446
+ reactFlightClientResponseRecord = RelayModernRecord.create(reactFlightID, REACT_FLIGHT_TYPE_NAME);
447
+
448
+ this._recordSource.set(reactFlightID, reactFlightClientResponseRecord);
449
+ }
450
+
451
+ RelayModernRecord.setValue(reactFlightClientResponseRecord, REACT_FLIGHT_TREE_STORAGE_KEY, reactFlightClientResponse);
452
+ var reachableQueries = [];
453
+
454
+ var _iterator = (0, _createForOfIteratorHelper2["default"])(reactFlightPayload.queries),
455
+ _step;
456
+
457
+ try {
458
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
459
+ var query = _step.value;
460
+
461
+ if (query.response.data != null) {
462
+ this._moduleImportPayloads.push({
463
+ data: query.response.data,
464
+ dataID: ROOT_ID,
465
+ operationReference: query.module,
466
+ path: [],
467
+ typeName: ROOT_TYPE,
468
+ variables: query.variables
469
+ });
470
+ }
471
+
472
+ reachableQueries.push({
473
+ module: query.module,
474
+ variables: query.variables
475
+ });
476
+ }
477
+ } catch (err) {
478
+ _iterator.e(err);
479
+ } finally {
480
+ _iterator.f();
481
+ }
482
+
483
+ RelayModernRecord.setValue(reactFlightClientResponseRecord, REACT_FLIGHT_QUERIES_STORAGE_KEY, reachableQueries);
484
+ RelayModernRecord.setLinkedRecordID(record, storageKey, reactFlightID);
485
+ };
486
+
397
487
  _proto._normalizeLink = function _normalizeLink(field, record, storageKey, fieldValue) {
398
488
  var _field$concreteType;
399
489
 
@@ -463,10 +553,14 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
463
553
  _this._recordSource.set(nextID, nextRecord);
464
554
  } else if (process.env.NODE_ENV !== "production") {
465
555
  _this._validateRecordType(nextRecord, field, item);
466
- }
556
+ } // NOTE: the check to strip __DEV__ code only works for simple
557
+ // `if (__DEV__)`
467
558
 
468
- if (prevIDs && process.env.NODE_ENV !== "production") {
469
- _this._validateConflictingLinkedFieldsWithIdenticalId(record, prevIDs[nextIndex], nextID, storageKey);
559
+
560
+ if (process.env.NODE_ENV !== "production") {
561
+ if (prevIDs) {
562
+ _this._validateConflictingLinkedFieldsWithIdenticalId(record, prevIDs[nextIndex], nextID, storageKey);
563
+ }
470
564
  } // $FlowFixMe[incompatible-variance]
471
565
 
472
566
 
@@ -494,10 +588,11 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
494
588
  ;
495
589
 
496
590
  _proto._validateConflictingFieldsWithIdenticalId = function _validateConflictingFieldsWithIdenticalId(record, storageKey, fieldValue) {
591
+ // NOTE: Only call this function in DEV
497
592
  if (process.env.NODE_ENV !== "production") {
498
593
  var dataID = RelayModernRecord.getDataID(record);
499
594
  var previousValue = RelayModernRecord.getValue(record, storageKey);
500
- process.env.NODE_ENV !== "production" ? warning(storageKey === TYPENAME_KEY || previousValue === undefined || previousValue === fieldValue, 'RelayResponseNormalizer: Invalid record. The record contains two ' + 'instances of the same id: `%s` with conflicting field, %s and its values: %s and %s. ' + 'If two fields are different but share ' + 'the same id, one field will overwrite the other.', dataID, storageKey, previousValue, fieldValue) : void 0;
595
+ process.env.NODE_ENV !== "production" ? warning(storageKey === TYPENAME_KEY || previousValue === undefined || areEqual(previousValue, fieldValue), 'RelayResponseNormalizer: Invalid record. The record contains two ' + 'instances of the same id: `%s` with conflicting field, %s and its values: %s and %s. ' + 'If two fields are different but share ' + 'the same id, one field will overwrite the other.', dataID, storageKey, previousValue, fieldValue) : void 0;
501
596
  }
502
597
  }
503
598
  /**
@@ -506,6 +601,7 @@ var RelayResponseNormalizer = /*#__PURE__*/function () {
506
601
  ;
507
602
 
508
603
  _proto._validateConflictingLinkedFieldsWithIdenticalId = function _validateConflictingLinkedFieldsWithIdenticalId(record, prevID, nextID, storageKey) {
604
+ // NOTE: Only call this function in DEV
509
605
  if (process.env.NODE_ENV !== "production") {
510
606
  process.env.NODE_ENV !== "production" ? warning(prevID === undefined || prevID === nextID, 'RelayResponseNormalizer: Invalid record. The record contains ' + 'references to the conflicting field, %s and its id values: %s and %s. ' + 'We need to make sure that the record the field points ' + 'to remains consistent or one field will overwrite the other.', storageKey, prevID, nextID) : void 0;
511
607
  }
@@ -0,0 +1,47 @@
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
+ *
8
+ * @format
9
+ */
10
+ // flowlint ambiguous-object-type:error
11
+ 'use strict';
12
+
13
+ var invariant = require("fbjs/lib/invariant");
14
+
15
+ var _require = require('./RelayModernRecord'),
16
+ getType = _require.getType;
17
+
18
+ var REACT_FLIGHT_QUERIES_STORAGE_KEY = 'queries';
19
+ var REACT_FLIGHT_TREE_STORAGE_KEY = 'tree';
20
+ var REACT_FLIGHT_TYPE_NAME = 'ReactFlightComponent';
21
+
22
+ function refineToReactFlightPayloadData(payload) {
23
+ if (payload == null || typeof payload !== 'object' || !Array.isArray(payload.tree) || !Array.isArray(payload.queries)) {
24
+ return null;
25
+ }
26
+
27
+ return payload;
28
+ }
29
+
30
+ function getReactFlightClientResponse(record) {
31
+ !(getType(record) === REACT_FLIGHT_TYPE_NAME) ? process.env.NODE_ENV !== "production" ? invariant(false, 'getReactFlightClientResponse(): Expected a ReactFlightComponentRecord, ' + 'got %s.', record) : invariant(false) : void 0;
32
+ var response = record[REACT_FLIGHT_TREE_STORAGE_KEY];
33
+
34
+ if (response != null) {
35
+ return response;
36
+ }
37
+
38
+ return null;
39
+ }
40
+
41
+ module.exports = {
42
+ REACT_FLIGHT_QUERIES_STORAGE_KEY: REACT_FLIGHT_QUERIES_STORAGE_KEY,
43
+ REACT_FLIGHT_TREE_STORAGE_KEY: REACT_FLIGHT_TREE_STORAGE_KEY,
44
+ REACT_FLIGHT_TYPE_NAME: REACT_FLIGHT_TYPE_NAME,
45
+ getReactFlightClientResponse: getReactFlightClientResponse,
46
+ refineToReactFlightPayloadData: refineToReactFlightPayloadData
47
+ };
@@ -0,0 +1,162 @@
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
+ *
8
+ * @format
9
+ */
10
+ // flowlint ambiguous-object-type:error
11
+ 'use strict';
12
+
13
+ var RelayReader = require('./RelayReader');
14
+
15
+ var deepFreeze = require('../util/deepFreeze');
16
+
17
+ var hasOverlappingIDs = require('./hasOverlappingIDs');
18
+
19
+ var isEmptyObject = require('../util/isEmptyObject');
20
+
21
+ var recycleNodesInto = require('../util/recycleNodesInto');
22
+
23
+ var RelayStoreSubscriptions = /*#__PURE__*/function () {
24
+ function RelayStoreSubscriptions() {
25
+ this._subscriptions = new Set();
26
+ }
27
+
28
+ var _proto = RelayStoreSubscriptions.prototype;
29
+
30
+ _proto.subscribe = function subscribe(snapshot, callback) {
31
+ var _this = this;
32
+
33
+ var subscription = {
34
+ backup: null,
35
+ callback: callback,
36
+ snapshot: snapshot,
37
+ stale: false
38
+ };
39
+
40
+ var dispose = function dispose() {
41
+ _this._subscriptions["delete"](subscription);
42
+ };
43
+
44
+ this._subscriptions.add(subscription);
45
+
46
+ return {
47
+ dispose: dispose
48
+ };
49
+ };
50
+
51
+ _proto.snapshotSubscriptions = function snapshotSubscriptions(source) {
52
+ this._subscriptions.forEach(function (subscription) {
53
+ // Backup occurs after writing a new "final" payload(s) and before (re)applying
54
+ // optimistic changes. Each subscription's `snapshot` represents what was *last
55
+ // published to the subscriber*, which notably may include previous optimistic
56
+ // updates. Therefore a subscription can be in any of the following states:
57
+ // - stale=true: This subscription was restored to a different value than
58
+ // `snapshot`. That means this subscription has changes relative to its base,
59
+ // but its base has changed (we just applied a final payload): recompute
60
+ // a backup so that we can later restore to the state the subscription
61
+ // should be in.
62
+ // - stale=false: This subscription was restored to the same value than
63
+ // `snapshot`. That means this subscription does *not* have changes relative
64
+ // to its base, so the current `snapshot` is valid to use as a backup.
65
+ if (!subscription.stale) {
66
+ subscription.backup = subscription.snapshot;
67
+ return;
68
+ }
69
+
70
+ var snapshot = subscription.snapshot;
71
+ var backup = RelayReader.read(source, snapshot.selector);
72
+ var nextData = recycleNodesInto(snapshot.data, backup.data);
73
+ backup.data = nextData; // backup owns the snapshot and can safely mutate
74
+
75
+ subscription.backup = backup;
76
+ });
77
+ };
78
+
79
+ _proto.restoreSubscriptions = function restoreSubscriptions() {
80
+ this._subscriptions.forEach(function (subscription) {
81
+ var backup = subscription.backup;
82
+ subscription.backup = null;
83
+
84
+ if (backup) {
85
+ if (backup.data !== subscription.snapshot.data) {
86
+ subscription.stale = true;
87
+ }
88
+
89
+ subscription.snapshot = {
90
+ data: subscription.snapshot.data,
91
+ isMissingData: backup.isMissingData,
92
+ seenRecords: backup.seenRecords,
93
+ selector: backup.selector,
94
+ missingRequiredFields: backup.missingRequiredFields
95
+ };
96
+ } else {
97
+ subscription.stale = true;
98
+ }
99
+ });
100
+ };
101
+
102
+ _proto.updateSubscriptions = function updateSubscriptions(source, updatedRecordIDs, updatedOwners) {
103
+ var _this2 = this;
104
+
105
+ var hasUpdatedRecords = !isEmptyObject(updatedRecordIDs);
106
+
107
+ this._subscriptions.forEach(function (subscription) {
108
+ var owner = _this2._updateSubscription(source, subscription, updatedRecordIDs, hasUpdatedRecords);
109
+
110
+ if (owner != null) {
111
+ updatedOwners.push(owner);
112
+ }
113
+ });
114
+ }
115
+ /**
116
+ * Notifies the callback for the subscription if the data for the associated
117
+ * snapshot has changed.
118
+ * Additionally, updates the subscription snapshot with the latest snapshot,
119
+ * and marks it as not stale.
120
+ * Returns the owner (RequestDescriptor) if the subscription was affected by the
121
+ * latest update, or null if it was not affected.
122
+ */
123
+ ;
124
+
125
+ _proto._updateSubscription = function _updateSubscription(source, subscription, updatedRecordIDs, hasUpdatedRecords) {
126
+ var backup = subscription.backup,
127
+ callback = subscription.callback,
128
+ snapshot = subscription.snapshot,
129
+ stale = subscription.stale;
130
+ var hasOverlappingUpdates = hasUpdatedRecords && hasOverlappingIDs(snapshot.seenRecords, updatedRecordIDs);
131
+
132
+ if (!stale && !hasOverlappingUpdates) {
133
+ return;
134
+ }
135
+
136
+ var nextSnapshot = hasOverlappingUpdates || !backup ? RelayReader.read(source, snapshot.selector) : backup;
137
+ var nextData = recycleNodesInto(snapshot.data, nextSnapshot.data);
138
+ nextSnapshot = {
139
+ data: nextData,
140
+ isMissingData: nextSnapshot.isMissingData,
141
+ seenRecords: nextSnapshot.seenRecords,
142
+ selector: nextSnapshot.selector,
143
+ missingRequiredFields: nextSnapshot.missingRequiredFields
144
+ };
145
+
146
+ if (process.env.NODE_ENV !== "production") {
147
+ deepFreeze(nextSnapshot);
148
+ }
149
+
150
+ subscription.snapshot = nextSnapshot;
151
+ subscription.stale = false;
152
+
153
+ if (nextSnapshot.data !== snapshot.data) {
154
+ callback(nextSnapshot);
155
+ return snapshot.selector.owner;
156
+ }
157
+ };
158
+
159
+ return RelayStoreSubscriptions;
160
+ }();
161
+
162
+ module.exports = RelayStoreSubscriptions;
@@ -0,0 +1,258 @@
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
+ *
8
+ * @format
9
+ */
10
+ // flowlint ambiguous-object-type:error
11
+ 'use strict';
12
+
13
+ var RelayReader = require('./RelayReader');
14
+
15
+ var deepFreeze = require('../util/deepFreeze');
16
+
17
+ var recycleNodesInto = require('../util/recycleNodesInto');
18
+
19
+ var RelayStoreSubscriptionsUsingMapByID = /*#__PURE__*/function () {
20
+ function RelayStoreSubscriptionsUsingMapByID() {
21
+ this._notifiedRevision = 0;
22
+ this._snapshotRevision = 0;
23
+ this._subscriptionsByDataId = new Map();
24
+ this._staleSubscriptions = new Set();
25
+ }
26
+
27
+ var _proto = RelayStoreSubscriptionsUsingMapByID.prototype;
28
+
29
+ _proto.subscribe = function subscribe(snapshot, callback) {
30
+ var _this = this;
31
+
32
+ var subscription = {
33
+ backup: null,
34
+ callback: callback,
35
+ notifiedRevision: this._notifiedRevision,
36
+ snapshotRevision: this._snapshotRevision,
37
+ snapshot: snapshot
38
+ };
39
+
40
+ var dispose = function dispose() {
41
+ for (var dataId in snapshot.seenRecords) {
42
+ var subscriptionsForDataId = _this._subscriptionsByDataId.get(dataId);
43
+
44
+ if (subscriptionsForDataId != null) {
45
+ subscriptionsForDataId["delete"](subscription);
46
+
47
+ if (subscriptionsForDataId.size === 0) {
48
+ _this._subscriptionsByDataId["delete"](dataId);
49
+ }
50
+ }
51
+ }
52
+ };
53
+
54
+ for (var dataId in snapshot.seenRecords) {
55
+ var subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
56
+
57
+ if (subscriptionsForDataId != null) {
58
+ subscriptionsForDataId.add(subscription);
59
+ } else {
60
+ this._subscriptionsByDataId.set(dataId, new Set([subscription]));
61
+ }
62
+ }
63
+
64
+ return {
65
+ dispose: dispose
66
+ };
67
+ };
68
+
69
+ _proto.snapshotSubscriptions = function snapshotSubscriptions(source) {
70
+ var _this2 = this;
71
+
72
+ this._snapshotRevision++;
73
+
74
+ this._subscriptionsByDataId.forEach(function (subscriptions) {
75
+ subscriptions.forEach(function (subscription) {
76
+ if (subscription.snapshotRevision === _this2._snapshotRevision) {
77
+ return;
78
+ }
79
+
80
+ subscription.snapshotRevision = _this2._snapshotRevision; // Backup occurs after writing a new "final" payload(s) and before (re)applying
81
+ // optimistic changes. Each subscription's `snapshot` represents what was *last
82
+ // published to the subscriber*, which notably may include previous optimistic
83
+ // updates. Therefore a subscription can be in any of the following states:
84
+ // - stale=true: This subscription was restored to a different value than
85
+ // `snapshot`. That means this subscription has changes relative to its base,
86
+ // but its base has changed (we just applied a final payload): recompute
87
+ // a backup so that we can later restore to the state the subscription
88
+ // should be in.
89
+ // - stale=false: This subscription was restored to the same value than
90
+ // `snapshot`. That means this subscription does *not* have changes relative
91
+ // to its base, so the current `snapshot` is valid to use as a backup.
92
+
93
+ if (!_this2._staleSubscriptions.has(subscription)) {
94
+ subscription.backup = subscription.snapshot;
95
+ return;
96
+ }
97
+
98
+ var snapshot = subscription.snapshot;
99
+ var backup = RelayReader.read(source, snapshot.selector);
100
+ var nextData = recycleNodesInto(snapshot.data, backup.data);
101
+ backup.data = nextData; // backup owns the snapshot and can safely mutate
102
+
103
+ subscription.backup = backup;
104
+ });
105
+ });
106
+ };
107
+
108
+ _proto.restoreSubscriptions = function restoreSubscriptions() {
109
+ var _this3 = this;
110
+
111
+ this._snapshotRevision++;
112
+
113
+ this._subscriptionsByDataId.forEach(function (subscriptions) {
114
+ subscriptions.forEach(function (subscription) {
115
+ if (subscription.snapshotRevision === _this3._snapshotRevision) {
116
+ return;
117
+ }
118
+
119
+ subscription.snapshotRevision = _this3._snapshotRevision;
120
+ var backup = subscription.backup;
121
+ subscription.backup = null;
122
+
123
+ if (backup) {
124
+ if (backup.data !== subscription.snapshot.data) {
125
+ _this3._staleSubscriptions.add(subscription);
126
+ }
127
+
128
+ var prevSeenRecords = subscription.snapshot.seenRecords;
129
+ subscription.snapshot = {
130
+ data: subscription.snapshot.data,
131
+ isMissingData: backup.isMissingData,
132
+ seenRecords: backup.seenRecords,
133
+ selector: backup.selector,
134
+ missingRequiredFields: backup.missingRequiredFields
135
+ };
136
+
137
+ _this3._updateSubscriptionsMap(subscription, prevSeenRecords);
138
+ } else {
139
+ _this3._staleSubscriptions.add(subscription);
140
+ }
141
+ });
142
+ });
143
+ };
144
+
145
+ _proto.updateSubscriptions = function updateSubscriptions(source, updatedRecordIDs, updatedOwners) {
146
+ var _this4 = this;
147
+
148
+ this._notifiedRevision++;
149
+ Object.keys(updatedRecordIDs).forEach(function (updatedRecordId) {
150
+ var subcriptionsForDataId = _this4._subscriptionsByDataId.get(updatedRecordId);
151
+
152
+ if (subcriptionsForDataId == null) {
153
+ return;
154
+ }
155
+
156
+ subcriptionsForDataId.forEach(function (subscription) {
157
+ if (subscription.notifiedRevision === _this4._notifiedRevision) {
158
+ return;
159
+ }
160
+
161
+ var owner = _this4._updateSubscription(source, subscription, false);
162
+
163
+ if (owner != null) {
164
+ updatedOwners.push(owner);
165
+ }
166
+ });
167
+ });
168
+
169
+ this._staleSubscriptions.forEach(function (subscription) {
170
+ if (subscription.notifiedRevision === _this4._notifiedRevision) {
171
+ return;
172
+ }
173
+
174
+ var owner = _this4._updateSubscription(source, subscription, true);
175
+
176
+ if (owner != null) {
177
+ updatedOwners.push(owner);
178
+ }
179
+ });
180
+
181
+ this._staleSubscriptions.clear();
182
+ }
183
+ /**
184
+ * Notifies the callback for the subscription if the data for the associated
185
+ * snapshot has changed.
186
+ * Additionally, updates the subscription snapshot with the latest snapshot,
187
+ * amarks it as not stale, and updates the subscription tracking for any
188
+ * any new ids observed in the latest data snapshot.
189
+ * Returns the owner (RequestDescriptor) if the subscription was affected by the
190
+ * latest update, or null if it was not affected.
191
+ */
192
+ ;
193
+
194
+ _proto._updateSubscription = function _updateSubscription(source, subscription, stale) {
195
+ var backup = subscription.backup,
196
+ callback = subscription.callback,
197
+ snapshot = subscription.snapshot;
198
+ var nextSnapshot = stale && backup != null ? backup : RelayReader.read(source, snapshot.selector);
199
+ var nextData = recycleNodesInto(snapshot.data, nextSnapshot.data);
200
+ nextSnapshot = {
201
+ data: nextData,
202
+ isMissingData: nextSnapshot.isMissingData,
203
+ seenRecords: nextSnapshot.seenRecords,
204
+ selector: nextSnapshot.selector,
205
+ missingRequiredFields: nextSnapshot.missingRequiredFields
206
+ };
207
+
208
+ if (process.env.NODE_ENV !== "production") {
209
+ deepFreeze(nextSnapshot);
210
+ }
211
+
212
+ var prevSeenRecords = subscription.snapshot.seenRecords;
213
+ subscription.snapshot = nextSnapshot;
214
+ subscription.notifiedRevision = this._notifiedRevision;
215
+
216
+ this._updateSubscriptionsMap(subscription, prevSeenRecords);
217
+
218
+ if (nextSnapshot.data !== snapshot.data) {
219
+ callback(nextSnapshot);
220
+ return snapshot.selector.owner;
221
+ }
222
+ }
223
+ /**
224
+ * Updates the Map that tracks subscriptions by id.
225
+ * Given an updated subscription and the records that where seen
226
+ * on the previous subscription snapshot, updates our tracking
227
+ * to track the subscription for the newly and no longer seen ids.
228
+ */
229
+ ;
230
+
231
+ _proto._updateSubscriptionsMap = function _updateSubscriptionsMap(subscription, prevSeenRecords) {
232
+ for (var dataId in prevSeenRecords) {
233
+ var subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
234
+
235
+ if (subscriptionsForDataId != null) {
236
+ subscriptionsForDataId["delete"](subscription);
237
+
238
+ if (subscriptionsForDataId.size === 0) {
239
+ this._subscriptionsByDataId["delete"](dataId);
240
+ }
241
+ }
242
+ }
243
+
244
+ for (var _dataId in subscription.snapshot.seenRecords) {
245
+ var _subscriptionsForDataId = this._subscriptionsByDataId.get(_dataId);
246
+
247
+ if (_subscriptionsForDataId != null) {
248
+ _subscriptionsForDataId.add(subscription);
249
+ } else {
250
+ this._subscriptionsByDataId.set(_dataId, new Set([subscription]));
251
+ }
252
+ }
253
+ };
254
+
255
+ return RelayStoreSubscriptionsUsingMapByID;
256
+ }();
257
+
258
+ module.exports = RelayStoreSubscriptionsUsingMapByID;