@airtable/blocks 1.10.2-experimental-640bd10-20220211 → 1.11.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 (85) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/dist/cjs/error_utils.js +9 -34
  3. package/dist/cjs/models/base.js +15 -0
  4. package/dist/cjs/models/field.js +99 -4
  5. package/dist/cjs/models/grouped_record_query_result.js +14 -5
  6. package/dist/cjs/models/linked_records_query_result.js +44 -86
  7. package/dist/cjs/models/mutations.js +234 -53
  8. package/dist/cjs/models/record.js +55 -310
  9. package/dist/cjs/models/record_query_result.js +1 -4
  10. package/dist/cjs/models/record_store.js +779 -557
  11. package/dist/cjs/models/table.js +6 -6
  12. package/dist/cjs/models/table_or_view_query_result.js +419 -526
  13. package/dist/cjs/models/view_data_store.js +295 -255
  14. package/dist/cjs/private_utils.js +0 -40
  15. package/dist/cjs/sdk.js +2 -12
  16. package/dist/cjs/testing/abstract_mock_airtable_interface.js +11 -57
  17. package/dist/cjs/types/airtable_interface.js +2 -17
  18. package/dist/cjs/types/mutations.js +1 -0
  19. package/dist/cjs/ui/use_records.js +1 -5
  20. package/dist/cjs/unstable_testing_utils.js +1 -55
  21. package/dist/cjs/watchable.js +1 -15
  22. package/dist/types/src/error_utils.d.ts +4 -2
  23. package/dist/types/src/error_utils.d.ts.map +1 -1
  24. package/dist/types/src/models/base.d.ts +10 -0
  25. package/dist/types/src/models/base.d.ts.map +1 -1
  26. package/dist/types/src/models/field.d.ts +58 -0
  27. package/dist/types/src/models/field.d.ts.map +1 -1
  28. package/dist/types/src/models/grouped_record_query_result.d.ts +3 -3
  29. package/dist/types/src/models/grouped_record_query_result.d.ts.map +1 -1
  30. package/dist/types/src/models/linked_records_query_result.d.ts.map +1 -1
  31. package/dist/types/src/models/mutations.d.ts.map +1 -1
  32. package/dist/types/src/models/record.d.ts +3 -12
  33. package/dist/types/src/models/record.d.ts.map +1 -1
  34. package/dist/types/src/models/record_query_result.d.ts +2 -3
  35. package/dist/types/src/models/record_query_result.d.ts.map +1 -1
  36. package/dist/types/src/models/record_store.d.ts.map +1 -1
  37. package/dist/types/src/models/table_or_view_query_result.d.ts +5 -3
  38. package/dist/types/src/models/table_or_view_query_result.d.ts.map +1 -1
  39. package/dist/types/src/models/view_data_store.d.ts +1 -0
  40. package/dist/types/src/models/view_data_store.d.ts.map +1 -1
  41. package/dist/types/src/models/view_metadata_query_result.d.ts +1 -1
  42. package/dist/types/src/models/view_metadata_query_result.d.ts.map +1 -1
  43. package/dist/types/src/private_utils.d.ts +1 -24
  44. package/dist/types/src/private_utils.d.ts.map +1 -1
  45. package/dist/types/src/sdk.d.ts.map +1 -1
  46. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts +11 -11
  47. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts.map +1 -1
  48. package/dist/types/src/types/airtable_interface.d.ts +20 -59
  49. package/dist/types/src/types/airtable_interface.d.ts.map +1 -1
  50. package/dist/types/src/types/base.d.ts +1 -0
  51. package/dist/types/src/types/base.d.ts.map +1 -1
  52. package/dist/types/src/types/mutations.d.ts +25 -2
  53. package/dist/types/src/types/mutations.d.ts.map +1 -1
  54. package/dist/types/src/types/table.d.ts +2 -0
  55. package/dist/types/src/types/table.d.ts.map +1 -1
  56. package/dist/types/src/types/view.d.ts +8 -3
  57. package/dist/types/src/types/view.d.ts.map +1 -1
  58. package/dist/types/src/ui/record_card.d.ts +1 -1
  59. package/dist/types/src/unstable_testing_utils.d.ts +1 -4
  60. package/dist/types/src/unstable_testing_utils.d.ts.map +1 -1
  61. package/dist/types/src/watchable.d.ts.map +1 -1
  62. package/dist/types/{src/testing → test/airtable_interface_mocks}/fixture_data.d.ts +19 -42
  63. package/dist/types/test/airtable_interface_mocks/fixture_data.d.ts.map +1 -0
  64. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts +1 -1
  65. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts.map +1 -1
  66. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts +12 -19
  67. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts.map +1 -1
  68. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts +1 -1
  69. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts.map +1 -1
  70. package/dist/types/test/test_helpers.d.ts +0 -2
  71. package/dist/types/test/test_helpers.d.ts.map +1 -1
  72. package/package.json +1 -2
  73. package/dist/cjs/models/query_manager.js +0 -328
  74. package/dist/cjs/testing/fixture_data.js +0 -268
  75. package/dist/cjs/testing/mock_base_data_stores.js +0 -876
  76. package/dist/cjs/types/block_query_spec.js +0 -85
  77. package/dist/types/src/models/query_manager.d.ts +0 -2
  78. package/dist/types/src/models/query_manager.d.ts.map +0 -1
  79. package/dist/types/src/testing/fixture_data.d.ts.map +0 -1
  80. package/dist/types/src/testing/mock_base_data_stores.d.ts +0 -55
  81. package/dist/types/src/testing/mock_base_data_stores.d.ts.map +0 -1
  82. package/dist/types/src/types/block_query_spec.d.ts +0 -139
  83. package/dist/types/src/types/block_query_spec.d.ts.map +0 -1
  84. package/dist/types/test/testing/fixture_data.test.d.ts +0 -2
  85. package/dist/types/test/testing/fixture_data.test.d.ts.map +0 -1
@@ -6,26 +6,18 @@ require("core-js/modules/es.symbol");
6
6
 
7
7
  require("core-js/modules/es.symbol.description");
8
8
 
9
- require("core-js/modules/es.array.concat");
10
-
11
9
  require("core-js/modules/es.array.filter");
12
10
 
13
11
  require("core-js/modules/es.array.iterator");
14
12
 
15
13
  require("core-js/modules/es.array.map");
16
14
 
17
- require("core-js/modules/es.object.get-own-property-descriptors");
18
-
19
15
  require("core-js/modules/es.object.to-string");
20
16
 
21
17
  require("core-js/modules/es.promise");
22
18
 
23
- require("core-js/modules/es.set");
24
-
25
19
  require("core-js/modules/es.string.starts-with");
26
20
 
27
- require("core-js/modules/web.dom-collections.for-each");
28
-
29
21
  require("core-js/modules/web.dom-collections.iterator");
30
22
 
31
23
  Object.defineProperty(exports, "__esModule", {
@@ -33,6 +25,8 @@ Object.defineProperty(exports, "__esModule", {
33
25
  });
34
26
  exports.default = exports.WatchableRecordStoreKeys = void 0;
35
27
 
28
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
29
+
36
30
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
37
31
 
38
32
  require("regenerator-runtime/runtime");
@@ -41,9 +35,11 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
41
35
 
42
36
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
43
37
 
38
+ var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
39
+
44
40
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
45
41
 
46
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
42
+ var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
47
43
 
48
44
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
49
45
 
@@ -55,20 +51,12 @@ var _private_utils = require("../private_utils");
55
51
 
56
52
  var _error_utils = require("../error_utils");
57
53
 
58
- var _warning = _interopRequireDefault(require("../warning"));
59
-
60
- var _block_query_spec = require("../types/block_query_spec");
61
-
62
54
  var _abstract_model_with_async_data = _interopRequireDefault(require("./abstract_model_with_async_data"));
63
55
 
64
56
  var _record = _interopRequireDefault(require("./record"));
65
57
 
66
58
  var _view_data_store = _interopRequireDefault(require("./view_data_store"));
67
59
 
68
- 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; }
69
-
70
- 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; }
71
-
72
60
  var WatchableRecordStoreKeys = Object.freeze({
73
61
  records: 'records',
74
62
  recordIds: 'recordIds',
@@ -83,9 +71,8 @@ var WatchableCellValuesInFieldKeyPrefix = 'cellValuesInField:';
83
71
  */
84
72
 
85
73
  /**
86
- * One RecordStore exists per table, and can access *some* of the record data associated with that
87
- * table, depending on which records are actually required for the slices of data that are subscribed.
88
- * Table itself is for schema information only, so isn't the appropriate place for record data.
74
+ * One RecordStore exists per table, and contains all the record data associated with that table.
75
+ * Table itself is for schema information only, so isn't the appropriate place for this data.
89
76
  *
90
77
  * @internal
91
78
  */
@@ -98,6 +85,16 @@ function (_AbstractModelWithAsy) {
98
85
  value: function _isWatchableKey(key) {
99
86
  return (0, _private_utils.isEnumValue)(WatchableRecordStoreKeys, key) || key.startsWith(WatchableCellValuesInFieldKeyPrefix);
100
87
  }
88
+ }, {
89
+ key: "_shouldLoadDataForKey",
90
+ value: function _shouldLoadDataForKey(key) {
91
+ // "Data" means *all* cell values in the table. If only watching records/recordIds,
92
+ // we'll just load record metadata (id, createdTime, commentCount).
93
+ // If only watching specific fields, we'll just load cell values in those
94
+ // fields. Both of those scenarios are handled manually by this class,
95
+ // instead of relying on AbstractModelWithAsyncData.
96
+ return key === WatchableRecordStoreKeys.cellValues;
97
+ }
101
98
  }]);
102
99
 
103
100
  function RecordStore(sdk, tableId) {
@@ -109,15 +106,11 @@ function (_AbstractModelWithAsy) {
109
106
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_recordModelsById", {});
110
107
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_primaryFieldId", void 0);
111
108
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_airtableInterface", void 0);
112
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_queryManager", void 0);
113
109
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_viewDataStoresByViewId", {});
114
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_airtableRecordStoreForTable", void 0);
115
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_numQueriesSubscribedToAllRecords", 0);
116
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_queryIdFromReferenceCountedLoadData", null);
117
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_loadedQuerySpecIds", new Set());
118
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_forceUnloadCallbacks", []);
110
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_areCellValuesLoadedByFieldId", {});
111
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_pendingCellValuesLoadPromiseByFieldId", {});
112
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_cellValuesRetainCountByFieldId", {});
119
113
  _this._airtableInterface = sdk.__airtableInterface;
120
- _this._queryManager = sdk._queryManager;
121
114
  _this.tableId = tableId; // A bit of a hack, but we use the primary field ID to load record
122
115
  // metadata (see _getFieldIdForCausingRecordMetadataToLoad). We copy the
123
116
  // ID here instead of calling this.primaryField.id since that would crash
@@ -128,323 +121,96 @@ function (_AbstractModelWithAsy) {
128
121
  }
129
122
 
130
123
  (0, _createClass2.default)(RecordStore, [{
131
- key: "_getRecordStoreForTable",
132
- value: function _getRecordStoreForTable() {
133
- if (!this._airtableRecordStoreForTable) {
134
- var airtableRecordStoreForTable = this._airtableInterface.getTableRecordStoreIfExists(this.tableId);
135
-
136
- (0, _error_utils.invariant)(airtableRecordStoreForTable, 'no RecordStoreForTable found for tableId %s', this.tableId);
137
- this._airtableRecordStoreForTable = airtableRecordStoreForTable;
138
- }
139
-
140
- return this._airtableRecordStoreForTable;
141
- }
142
- }, {
143
124
  key: "getViewDataStore",
144
125
  value: function getViewDataStore(viewId) {
145
126
  if (this._viewDataStoresByViewId[viewId]) {
146
127
  return this._viewDataStoresByViewId[viewId];
147
128
  }
148
129
 
149
- (0, _error_utils.invariant)(this._data.viewsById[viewId], 'view must exist'); // TODO: (#proj-blocks-sdk-record-limits) Rename ViewDataStore so that it does not
150
- // conflict with hyperbase ViewDataStore. Here we are using the SDK ViewDataStore
151
- // which is a thin wrapper around the hyperbase one.
152
-
130
+ (0, _error_utils.invariant)(this._data.viewsById[viewId], 'view must exist');
153
131
  var viewDataStore = new _view_data_store.default(this._sdk, this, viewId);
154
132
  this._viewDataStoresByViewId[viewId] = viewDataStore;
155
133
  return viewDataStore;
156
134
  }
157
135
  }, {
158
- key: "sendViewDataDeletionIfViewIsDeleted",
159
- value: function sendViewDataDeletionIfViewIsDeleted(viewId) {
160
- var viewDataStoreIfExists = this._viewDataStoresByViewId[viewId];
161
-
162
- if (viewDataStoreIfExists && viewDataStoreIfExists.isDeleted) {
163
- viewDataStoreIfExists.__onDataDeletion();
164
-
165
- delete this._viewDataStoresByViewId[viewId];
166
- }
167
- }
168
- }, {
169
- key: "_findMatchingBlockSubscriptionChanges",
170
- value: function _findMatchingBlockSubscriptionChanges(changes, changeTypeToMatch) {
171
- return changes.filter(change => change.type === changeTypeToMatch && change.tableId === this.tableId);
172
- }
173
- }, {
174
- key: "_constructBlockQuerySpecAndCallbackForWatchableKeyWithRecordQueryResult",
175
- value: function _constructBlockQuerySpecAndCallbackForWatchableKeyWithRecordQueryResult(recordQueryResult, key, callbackForRegistration, context) {
176
- var fieldSelectionDefinition;
177
- var originThis = recordQueryResult;
178
- var onChangeCallback;
179
-
180
- switch (key) {
181
- case WatchableRecordStoreKeys.records:
182
- case WatchableRecordStoreKeys.recordIds:
183
- fieldSelectionDefinition = {
184
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
185
- fieldIds: []
186
- };
187
-
188
- onChangeCallback = changes => {
189
- var addedRecordsChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.ADDED_RECORD_IDS);
190
-
191
- var removedRecordsChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.REMOVED_RECORD_IDS);
192
-
193
- var addedRecordIds = [];
194
- var removedRecordIds = [];
195
- var _iteratorNormalCompletion = true;
196
- var _didIteratorError = false;
197
- var _iteratorError = undefined;
198
-
199
- try {
200
- for (var _iterator = addedRecordsChanges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
201
- var addedRecordsChange = _step.value;
202
- (0, _error_utils.invariant)(addedRecordsChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.ADDED_RECORD_IDS, 'Added Records change is not a supported type');
203
- addedRecordIds = addedRecordIds.concat(addedRecordsChange.recordIds);
204
- }
205
- } catch (err) {
206
- _didIteratorError = true;
207
- _iteratorError = err;
208
- } finally {
209
- try {
210
- if (!_iteratorNormalCompletion && _iterator.return != null) {
211
- _iterator.return();
212
- }
213
- } finally {
214
- if (_didIteratorError) {
215
- throw _iteratorError;
216
- }
217
- }
218
- }
219
-
220
- var _iteratorNormalCompletion2 = true;
221
- var _didIteratorError2 = false;
222
- var _iteratorError2 = undefined;
223
-
224
- try {
225
- for (var _iterator2 = removedRecordsChanges[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
226
- var removedRecordsChange = _step2.value;
227
- (0, _error_utils.invariant)(removedRecordsChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.REMOVED_RECORD_IDS, 'Removed Records change is not a supported type');
228
- removedRecordIds = removedRecordIds.concat(removedRecordsChange.recordIds);
229
- }
230
- } catch (err) {
231
- _didIteratorError2 = true;
232
- _iteratorError2 = err;
233
- } finally {
234
- try {
235
- if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
236
- _iterator2.return();
237
- }
238
- } finally {
239
- if (_didIteratorError2) {
240
- throw _iteratorError2;
241
- }
242
- }
243
- }
244
-
245
- var recordIdsChanged = {
246
- addedRecordIds,
247
- removedRecordIds
248
- };
249
- callbackForRegistration.call(context, originThis, key, recordIdsChanged);
250
- };
251
-
252
- break;
253
-
254
- case WatchableRecordStoreKeys.cellValues:
255
- fieldSelectionDefinition = {
256
- type: _block_query_spec.BlockFieldSelectionSpecType.ALL_FIELDS_FROM_TABLE
257
- };
258
-
259
- onChangeCallback = changes => {
260
- var modifiedCellValuesChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE);
261
-
262
- if (modifiedCellValuesChanges.length === 0) {
263
- callbackForRegistration.call(context, originThis, key);
264
- return;
265
- }
266
-
267
- var recordsAndFieldsChanged = {
268
- recordIds: [],
269
- fieldIds: []
270
- };
271
- var _iteratorNormalCompletion3 = true;
272
- var _didIteratorError3 = false;
273
- var _iteratorError3 = undefined;
274
-
275
- try {
276
- for (var _iterator3 = modifiedCellValuesChanges[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
277
- var modifiedCellValuesChange = _step3.value;
278
- (0, _error_utils.invariant)(modifiedCellValuesChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE, 'Modified cell value change is not a supported type');
279
- var fieldIdsByRecordId = modifiedCellValuesChange.fieldIdsByRecordId,
280
- fieldIds = modifiedCellValuesChange.fieldIds;
281
- recordsAndFieldsChanged.recordIds = recordsAndFieldsChanged.recordIds.concat(Object.keys(fieldIdsByRecordId));
282
- recordsAndFieldsChanged.fieldIds = recordsAndFieldsChanged.fieldIds.concat(fieldIds);
283
- }
284
- } catch (err) {
285
- _didIteratorError3 = true;
286
- _iteratorError3 = err;
287
- } finally {
288
- try {
289
- if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
290
- _iterator3.return();
291
- }
292
- } finally {
293
- if (_didIteratorError3) {
294
- throw _iteratorError3;
295
- }
296
- }
297
- }
298
-
299
- callbackForRegistration.call(context, originThis, key, recordsAndFieldsChanged);
300
- };
301
-
302
- break;
303
-
304
- default:
305
- {
306
- // Since this watch class is only called internally from within the SDK
307
- // an error here is an internal SDK error.
308
- (0, _error_utils.invariant)(key.startsWith(WatchableCellValuesInFieldKeyPrefix), 'Internal error - Unrecognized watch key on record_store: %s', key);
309
- var fieldId = key.substring(WatchableCellValuesInFieldKeyPrefix.length); // Assert that fieldId is validly formatted, not that it is a valid fieldId in this table
310
-
311
- (0, _error_utils.invariant)((0, _private_utils.isFieldId)(fieldId), 'fieldId passed as watchable fieldId is not a valid format' + fieldId);
312
- fieldSelectionDefinition = {
313
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
314
- fieldIds: [fieldId]
315
- };
316
-
317
- onChangeCallback = changes => {
318
- var modifiedCellValuesChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE);
319
-
320
- if (modifiedCellValuesChanges.length === 0) {
321
- callbackForRegistration.call(context, originThis, key);
322
- return;
323
- }
324
-
325
- var recordsChanged = [];
326
- var _iteratorNormalCompletion4 = true;
327
- var _didIteratorError4 = false;
328
- var _iteratorError4 = undefined;
329
-
330
- try {
331
- for (var _iterator4 = modifiedCellValuesChanges[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
332
- var modifiedCellValuesChange = _step4.value;
333
- (0, _error_utils.invariant)(modifiedCellValuesChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE, 'Modified cell value change is not a supported type');
334
- recordsChanged = recordsChanged.concat(Object.keys(modifiedCellValuesChange.fieldIdsByRecordId));
335
- } // Why is this callback format different than when watching just cellValues?
336
- // It's because in the past this was the format we used: https://github.com/Hyperbase/blocks-sdk/blob/32b6c59944f0d89d2c0b3b61b7db615145a8856b/packages/sdk/src/models/record_store.ts#L547-L554
337
-
338
- } catch (err) {
339
- _didIteratorError4 = true;
340
- _iteratorError4 = err;
341
- } finally {
342
- try {
343
- if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
344
- _iterator4.return();
345
- }
346
- } finally {
347
- if (_didIteratorError4) {
348
- throw _iteratorError4;
349
- }
350
- }
351
- }
136
+ key: "watch",
137
+ value: function watch(keys, callback, context) {
138
+ var validKeys = (0, _get2.default)((0, _getPrototypeOf2.default)(RecordStore.prototype), "watch", this).call(this, keys, callback, context);
352
139
 
353
- callbackForRegistration.call(context, originThis, key, recordsChanged, fieldId);
354
- };
140
+ var fieldIdsToLoad = this._getFieldIdsToLoadFromWatchableKeys(validKeys);
355
141
 
356
- break;
357
- }
142
+ if (fieldIdsToLoad.length > 0) {
143
+ (0, _private_utils.fireAndForgetPromise)(this.loadCellValuesInFieldIdsAsync.bind(this, fieldIdsToLoad));
358
144
  }
359
145
 
360
- return {
361
- querySpecToSubscribeWith: recordQueryResult.__constructQuerySpecForBlockFieldSelectionSpec(fieldSelectionDefinition),
362
- onChangeCallback
363
- };
364
- }
365
- }, {
366
- key: "watch",
367
- value: function watch(keys, callbackForRegistration, context) {
368
- throw (0, _error_utils.spawnError)('Must not call watch on record_store directly');
369
- }
370
- }, {
371
- key: "watchWithRecordQueryResult",
372
- value: function watchWithRecordQueryResult(_ref) {
373
- var priority = _ref.priority,
374
- recordQueryResult = _ref.recordQueryResult,
375
- keys = _ref.keys,
376
- callbackForRegistration = _ref.callbackForRegistration,
377
- context = _ref.context;
378
-
379
- // Note that we do not call super.watch(), we use the
380
- // alternative this._queryManager.watchWithQuerySpec
381
- this._assertNotForceUnloaded();
382
-
383
- var validKeys = this._getWatchableValidKeysOrThrow(keys, 'table watch');
384
-
385
- validKeys.forEach(validKey => {
386
- var _this$_constructBlock = this._constructBlockQuerySpecAndCallbackForWatchableKeyWithRecordQueryResult(recordQueryResult, validKey, callbackForRegistration, context),
387
- querySpecToSubscribeWith = _this$_constructBlock.querySpecToSubscribeWith,
388
- onChangeCallback = _this$_constructBlock.onChangeCallback;
389
-
390
- var onChangeCallbackThatIncrementsWatchChangeCount = args => {
391
- // Normally calling __incrementOnChangeCount is handled by queryManager.watchWithQuerySpec
392
- // however it will only increment the change count on the recordStore. In this case we ALSO
393
- // need to increment the change count on the recordQueryResult.
394
- recordQueryResult.__incrementOnChangeCount();
395
-
396
- onChangeCallback(args);
397
- };
398
-
399
- this._queryManager.watchWithQuerySpec(priority, querySpecToSubscribeWith, this, validKey, onChangeCallbackThatIncrementsWatchChangeCount, callbackForRegistration, context);
400
- });
401
146
  return validKeys;
402
147
  }
403
148
  }, {
404
149
  key: "unwatch",
405
- value: function unwatch(keys, callbackForRegistration, context) {
406
- // We have already unsubscribed from all watches (because query-based watches force data to be loaded)
407
- // so no watches are subscribed
408
- if (this._isForceUnloaded) {
409
- return [];
410
- } // We warn instead of throw here because we used to warn instead of throw and don't
411
- // want to make a breaking change.
150
+ value: function unwatch(keys, callback, context) {
151
+ var validKeys = (0, _get2.default)((0, _getPrototypeOf2.default)(RecordStore.prototype), "unwatch", this).call(this, keys, callback, context);
412
152
 
153
+ var fieldIdsToUnload = this._getFieldIdsToLoadFromWatchableKeys(validKeys);
413
154
 
414
- var validKeys = this._getWatchableValidKeysOrThrow(keys, 'table unwatch', true);
155
+ if (fieldIdsToUnload.length > 0) {
156
+ this.unloadCellValuesInFieldIds(fieldIdsToUnload);
157
+ }
415
158
 
416
- validKeys.forEach(key => this._queryManager.unwatchFromQueryKey(key, callbackForRegistration, context));
417
159
  return validKeys;
418
160
  }
419
161
  }, {
420
- key: "isRecordDeleted",
421
- value: function isRecordDeleted(recordId) {
422
- return this._getRecordStoreForTable().isRecordDeleted(recordId);
423
- } // Maybe add a comment "pending removal of inheriting from parent class AbstractModelWithAsyncData"
424
- // istanbul ignore next
162
+ key: "_getFieldIdsToLoadFromWatchableKeys",
163
+ value: function _getFieldIdsToLoadFromWatchableKeys(keys) {
164
+ var fieldIdsToLoad = [];
165
+ var _iteratorNormalCompletion = true;
166
+ var _didIteratorError = false;
167
+ var _iteratorError = undefined;
168
+
169
+ try {
170
+ for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
171
+ var key = _step.value;
172
+
173
+ if (key.startsWith(WatchableCellValuesInFieldKeyPrefix)) {
174
+ var fieldId = key.substring(WatchableCellValuesInFieldKeyPrefix.length);
175
+ fieldIdsToLoad.push(fieldId);
176
+ } else if (key === WatchableRecordStoreKeys.records || key === WatchableRecordStoreKeys.recordIds) {
177
+ fieldIdsToLoad.push(this._getFieldIdForCausingRecordMetadataToLoad());
178
+ }
179
+ }
180
+ } catch (err) {
181
+ _didIteratorError = true;
182
+ _iteratorError = err;
183
+ } finally {
184
+ try {
185
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
186
+ _iterator.return();
187
+ }
188
+ } finally {
189
+ if (_didIteratorError) {
190
+ throw _iteratorError;
191
+ }
192
+ }
193
+ }
425
194
 
195
+ return fieldIdsToLoad;
196
+ }
426
197
  }, {
427
198
  key: "_onChangeIsDataLoaded",
428
- value: function _onChangeIsDataLoaded() {
429
- throw (0, _error_utils.spawnError)('internal error - onChangeIsDataLoaded illegal to call on record_store');
430
- }
199
+ value: function _onChangeIsDataLoaded() {} // noop
200
+
431
201
  /**
432
202
  * The records in this table. The order is arbitrary since records are
433
203
  * only ordered in the context of a specific view.
434
204
  */
435
205
 
436
- }, {
437
- key: "isRecordLoaded",
438
- value: function isRecordLoaded(recordId) {
439
- return this._getRecordStoreForTable().isRecordPartiallyLoaded(recordId);
440
- }
441
206
  }, {
442
207
  key: "getRecordByIdIfExists",
443
208
  value: function getRecordByIdIfExists(recordId) {
444
- (0, _error_utils.invariant)(this._loadedQuerySpecIds.size > 0, 'Record metadata is not loaded');
209
+ var recordsById = this._data.recordsById;
210
+ (0, _error_utils.invariant)(recordsById, 'Record metadata is not loaded');
445
211
  (0, _error_utils.invariant)(typeof recordId === 'string', 'getRecordById expects a string');
446
212
 
447
- if (!this._getRecordStoreForTable().isRecordPartiallyLoaded(recordId)) {
213
+ if (!recordsById[recordId]) {
448
214
  return null;
449
215
  } else {
450
216
  if (this._recordModelsById[recordId]) {
@@ -459,64 +225,76 @@ function (_AbstractModelWithAsy) {
459
225
  }, {
460
226
  key: "__onDataDeletion",
461
227
  value: function __onDataDeletion() {
462
- this._forceUnload();
228
+ // also need to call unloadCellValuesInFieldIds because otherwise
229
+ // on the hyperbase side, the old record store would still be subscribed
230
+ // to the cell values and it will refuse a request for new subscription
231
+ for (var _i = 0, _Object$keys = Object.keys(this._cellValuesRetainCountByFieldId); _i < _Object$keys.length; _i++) {
232
+ var fieldId = _Object$keys[_i];
233
+
234
+ while (this._cellValuesRetainCountByFieldId[fieldId] && this._cellValuesRetainCountByFieldId[fieldId] > 0) {
235
+ this.unloadCellValuesInFieldIds([fieldId]);
236
+ }
237
+ }
238
+
239
+ this._forceUnload(); // similarly unsubscribe from the view data.
240
+ // this comes after _forceUnload to avoid over releasing the table data.
463
241
 
464
- this._queryManager.forceUnloadWatchesOnWatchableModel(this);
465
242
 
466
- var _iteratorNormalCompletion5 = true;
467
- var _didIteratorError5 = false;
468
- var _iteratorError5 = undefined;
243
+ var _iteratorNormalCompletion2 = true;
244
+ var _didIteratorError2 = false;
245
+ var _iteratorError2 = undefined;
469
246
 
470
247
  try {
471
- for (var _iterator5 = this._forceUnloadCallbacks[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
472
- var forceUnloadCallback = _step5.value;
473
- forceUnloadCallback();
474
- } // similarly unsubscribe from the view data.
475
- // this comes after _forceUnload to avoid over releasing the table data.
248
+ for (var _iterator2 = (0, _private_utils.values)(this._viewDataStoresByViewId)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
249
+ var viewDataStore = _step2.value;
476
250
 
251
+ viewDataStore.__onDataDeletion();
252
+ }
477
253
  } catch (err) {
478
- _didIteratorError5 = true;
479
- _iteratorError5 = err;
254
+ _didIteratorError2 = true;
255
+ _iteratorError2 = err;
480
256
  } finally {
481
257
  try {
482
- if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
483
- _iterator5.return();
258
+ if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
259
+ _iterator2.return();
484
260
  }
485
261
  } finally {
486
- if (_didIteratorError5) {
487
- throw _iteratorError5;
262
+ if (_didIteratorError2) {
263
+ throw _iteratorError2;
488
264
  }
489
265
  }
490
266
  }
267
+ }
268
+ /**
269
+ * Record metadata means record IDs, createdTime, and commentCount are loaded.
270
+ * Record metadata must be loaded before creating, deleting, or updating records.
271
+ */
491
272
 
492
- var _iteratorNormalCompletion6 = true;
493
- var _didIteratorError6 = false;
494
- var _iteratorError6 = undefined;
273
+ }, {
274
+ key: "loadRecordMetadataAsync",
275
+ value: function loadRecordMetadataAsync() {
276
+ return _regenerator.default.async(function loadRecordMetadataAsync$(_context) {
277
+ while (1) {
278
+ switch (_context.prev = _context.next) {
279
+ case 0:
280
+ _context.next = 2;
281
+ return _regenerator.default.awrap(this.loadCellValuesInFieldIdsAsync([this._getFieldIdForCausingRecordMetadataToLoad()]));
495
282
 
496
- try {
497
- for (var _iterator6 = (0, _private_utils.values)(this._viewDataStoresByViewId)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
498
- var viewDataStore = _step6.value;
283
+ case 2:
284
+ return _context.abrupt("return", _context.sent);
499
285
 
500
- viewDataStore.__onDataDeletion();
501
- }
502
- } catch (err) {
503
- _didIteratorError6 = true;
504
- _iteratorError6 = err;
505
- } finally {
506
- try {
507
- if (!_iteratorNormalCompletion6 && _iterator6.return != null) {
508
- _iterator6.return();
509
- }
510
- } finally {
511
- if (_didIteratorError6) {
512
- throw _iteratorError6;
286
+ case 3:
287
+ case "end":
288
+ return _context.stop();
513
289
  }
514
290
  }
515
- }
516
- } // TODO: (#proj-blocks-sdk-record-limits) Determine if we need this function,
517
- // we shouldn't need to load the primary field in order to load metadata.
518
- // (but may want to require it for the first version of the sdk we release)
519
-
291
+ }, null, this);
292
+ }
293
+ }, {
294
+ key: "unloadRecordMetadata",
295
+ value: function unloadRecordMetadata() {
296
+ this.unloadCellValuesInFieldIds([this._getFieldIdForCausingRecordMetadataToLoad()]);
297
+ }
520
298
  }, {
521
299
  key: "_getFieldIdForCausingRecordMetadataToLoad",
522
300
  value: function _getFieldIdForCausingRecordMetadataToLoad() {
@@ -527,276 +305,721 @@ function (_AbstractModelWithAsy) {
527
305
  return this._primaryFieldId;
528
306
  }
529
307
  }, {
530
- key: "isRecordCellValueLoadedForFieldId",
531
- value: function isRecordCellValueLoadedForFieldId(recordId, fieldId) {
532
- return this._getRecordStoreForTable().isRecordCellValueLoadedForFieldId(recordId, fieldId);
533
- }
534
- }, {
535
- key: "getRecordCellValueByFieldId",
536
- value: function getRecordCellValueByFieldId(recordId, fieldId) {
537
- return this._getRecordStoreForTable().getRecordCellValueByFieldId(recordId, fieldId);
308
+ key: "areCellValuesLoadedForFieldId",
309
+ value: function areCellValuesLoadedForFieldId(fieldId) {
310
+ return this.isDataLoaded || this._areCellValuesLoadedByFieldId[fieldId] || false;
538
311
  }
539
312
  }, {
540
- key: "getRecordCommentCount",
541
- value: function getRecordCommentCount(recordId) {
542
- return this._getRecordStoreForTable().getRecordCommentCount(recordId);
543
- }
544
- }, {
545
- key: "getRecordCreatedTime",
546
- value: function getRecordCreatedTime(recordId) {
547
- return this._getRecordStoreForTable().getRecordCreatedTime(recordId);
548
- }
549
- }, {
550
- key: "_loadCellValuesForQueryAsync",
551
- value: function _loadCellValuesForQueryAsync(query) {
552
- var querySpecIdPromise, onForceUnload, querySpecId;
553
- return _regenerator.default.async(function _loadCellValuesForQueryAsync$(_context2) {
313
+ key: "loadCellValuesInFieldIdsAsync",
314
+ value: function loadCellValuesInFieldIdsAsync(fieldIds) {
315
+ var fieldIdsWhichAreNotAlreadyLoadedOrLoading, pendingLoadPromises, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, _fieldId, pendingLoadPromise, loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, fieldId;
316
+
317
+ return _regenerator.default.async(function loadCellValuesInFieldIdsAsync$(_context2) {
554
318
  while (1) {
555
319
  switch (_context2.prev = _context2.next) {
556
320
  case 0:
557
- this._assertNotForceUnloaded(); // We send all queries through query manager. PublicAirtableInterface does
558
- // all reference counting for queries, and doesn't send cell data over the bridge
559
- // if it's already being tracked in the PublicRecordStore.
560
-
561
-
562
- querySpecIdPromise = this._sdk._queryManager.fetchAndSubscribeToQueryAsync(_objectSpread({}, query, {
563
- sourceTableId: this.tableId
564
- }));
565
-
566
- onForceUnload = () => {
567
- var querySpecId;
568
- return _regenerator.default.async(function _callee$(_context) {
569
- while (1) {
570
- switch (_context.prev = _context.next) {
571
- case 0:
572
- _context.next = 2;
573
- return _regenerator.default.awrap(querySpecIdPromise);
574
-
575
- case 2:
576
- querySpecId = _context.sent;
577
-
578
- if (this._loadedQuerySpecIds.has(querySpecId)) {
579
- this._unloadCellValuesForQueryIdsIgnoringForceUnloadedness([querySpecId]);
580
- }
581
-
582
- case 4:
583
- case "end":
584
- return _context.stop();
585
- }
321
+ this._assertNotForceUnloaded();
322
+
323
+ fieldIdsWhichAreNotAlreadyLoadedOrLoading = [];
324
+ pendingLoadPromises = [];
325
+ _iteratorNormalCompletion3 = true;
326
+ _didIteratorError3 = false;
327
+ _iteratorError3 = undefined;
328
+ _context2.prev = 6;
329
+
330
+ for (_iterator3 = fieldIds[Symbol.iterator](); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
331
+ _fieldId = _step3.value;
332
+
333
+ if (this._cellValuesRetainCountByFieldId[_fieldId] !== undefined) {
334
+ this._cellValuesRetainCountByFieldId[_fieldId]++;
335
+ } else {
336
+ this._cellValuesRetainCountByFieldId[_fieldId] = 1;
337
+ } // NOTE: we don't use this.areCellValuesLoadedForFieldId() here because
338
+ // that will return true if the cell values are loaded as a result
339
+ // of the entire table being loaded. In that scenario, we still
340
+ // want to separately load the cell values for the field so there
341
+ // is a separate subscription. Otherwise, when the table data unloads,
342
+ // the field data would unload as well. This can be improved by just
343
+ // subscribing to the field data without fetching it, since the cell
344
+ // values are already in the block frame.
345
+
346
+
347
+ if (!this._areCellValuesLoadedByFieldId[_fieldId]) {
348
+ pendingLoadPromise = this._pendingCellValuesLoadPromiseByFieldId[_fieldId];
349
+
350
+ if (pendingLoadPromise) {
351
+ pendingLoadPromises.push(pendingLoadPromise);
352
+ } else {
353
+ fieldIdsWhichAreNotAlreadyLoadedOrLoading.push(_fieldId);
586
354
  }
587
- }, null, this);
588
- };
355
+ }
356
+ }
357
+
358
+ _context2.next = 14;
359
+ break;
360
+
361
+ case 10:
362
+ _context2.prev = 10;
363
+ _context2.t0 = _context2["catch"](6);
364
+ _didIteratorError3 = true;
365
+ _iteratorError3 = _context2.t0;
589
366
 
590
- this._forceUnloadCallbacks.push(onForceUnload);
367
+ case 14:
368
+ _context2.prev = 14;
369
+ _context2.prev = 15;
591
370
 
592
- _context2.next = 6;
593
- return _regenerator.default.awrap(querySpecIdPromise);
371
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
372
+ _iterator3.return();
373
+ }
374
+
375
+ case 17:
376
+ _context2.prev = 17;
377
+
378
+ if (!_didIteratorError3) {
379
+ _context2.next = 20;
380
+ break;
381
+ }
382
+
383
+ throw _iteratorError3;
384
+
385
+ case 20:
386
+ return _context2.finish(17);
387
+
388
+ case 21:
389
+ return _context2.finish(14);
390
+
391
+ case 22:
392
+ if (!(fieldIdsWhichAreNotAlreadyLoadedOrLoading.length > 0)) {
393
+ _context2.next = 45;
394
+ break;
395
+ }
396
+
397
+ // Could inline _loadCellValuesInFieldIdsAsync, but following the
398
+ // pattern from AbstractModelWithAsyncData where the public method
399
+ // is responsible for updating retain counts and the private method
400
+ // actually fetches data.
401
+ loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise = this._loadCellValuesInFieldIdsAsync(fieldIdsWhichAreNotAlreadyLoadedOrLoading);
402
+ pendingLoadPromises.push(loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise);
403
+ _iteratorNormalCompletion4 = true;
404
+ _didIteratorError4 = false;
405
+ _iteratorError4 = undefined;
406
+ _context2.prev = 28;
407
+
408
+ for (_iterator4 = fieldIdsWhichAreNotAlreadyLoadedOrLoading[Symbol.iterator](); !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
409
+ fieldId = _step4.value;
410
+ this._pendingCellValuesLoadPromiseByFieldId[fieldId] = loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise;
411
+ } // Doing `.then` instead of performing these actions directly in
412
+ // _loadCellValuesInFieldIdsAsync so this is similar to
413
+ // AbstractModelWithAsyncData. The idea is to refactor to avoid code
414
+ // duplication, so keeping them similar for now hopefully will make the
415
+ // refactor simpler.
416
+
417
+
418
+ _context2.next = 36;
419
+ break;
420
+
421
+ case 32:
422
+ _context2.prev = 32;
423
+ _context2.t1 = _context2["catch"](28);
424
+ _didIteratorError4 = true;
425
+ _iteratorError4 = _context2.t1;
426
+
427
+ case 36:
428
+ _context2.prev = 36;
429
+ _context2.prev = 37;
430
+
431
+ if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
432
+ _iterator4.return();
433
+ }
594
434
 
595
- case 6:
596
- querySpecId = _context2.sent;
435
+ case 39:
436
+ _context2.prev = 39;
597
437
 
598
- this._loadedQuerySpecIds.add(querySpecId);
438
+ if (!_didIteratorError4) {
439
+ _context2.next = 42;
440
+ break;
441
+ }
442
+
443
+ throw _iteratorError4;
444
+
445
+ case 42:
446
+ return _context2.finish(39);
599
447
 
600
- return _context2.abrupt("return", querySpecId);
448
+ case 43:
449
+ return _context2.finish(36);
601
450
 
602
- case 9:
451
+ case 44:
452
+ loadFieldsWhichAreNotAlreadyLoadedOrLoadingPromise.then(changedKeys => {
453
+ var _iteratorNormalCompletion5 = true;
454
+ var _didIteratorError5 = false;
455
+ var _iteratorError5 = undefined;
456
+
457
+ try {
458
+ for (var _iterator5 = fieldIdsWhichAreNotAlreadyLoadedOrLoading[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
459
+ var fieldId = _step5.value;
460
+ this._areCellValuesLoadedByFieldId[fieldId] = true;
461
+ this._pendingCellValuesLoadPromiseByFieldId[fieldId] = undefined;
462
+ }
463
+ } catch (err) {
464
+ _didIteratorError5 = true;
465
+ _iteratorError5 = err;
466
+ } finally {
467
+ try {
468
+ if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
469
+ _iterator5.return();
470
+ }
471
+ } finally {
472
+ if (_didIteratorError5) {
473
+ throw _iteratorError5;
474
+ }
475
+ }
476
+ }
477
+
478
+ var _iteratorNormalCompletion6 = true;
479
+ var _didIteratorError6 = false;
480
+ var _iteratorError6 = undefined;
481
+
482
+ try {
483
+ for (var _iterator6 = changedKeys[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
484
+ var key = _step6.value;
485
+
486
+ this._onChange(key);
487
+ }
488
+ } catch (err) {
489
+ _didIteratorError6 = true;
490
+ _iteratorError6 = err;
491
+ } finally {
492
+ try {
493
+ if (!_iteratorNormalCompletion6 && _iterator6.return != null) {
494
+ _iterator6.return();
495
+ }
496
+ } finally {
497
+ if (_didIteratorError6) {
498
+ throw _iteratorError6;
499
+ }
500
+ }
501
+ }
502
+ });
503
+
504
+ case 45:
505
+ _context2.next = 47;
506
+ return _regenerator.default.awrap(Promise.all(pendingLoadPromises));
507
+
508
+ case 47:
603
509
  case "end":
604
510
  return _context2.stop();
605
511
  }
606
512
  }
607
- }, null, this);
513
+ }, null, this, [[6, 10, 14, 22], [15,, 17, 21], [28, 32, 36, 44], [37,, 39, 43]]);
608
514
  }
609
515
  }, {
610
- key: "_unloadCellValuesForQueryIds",
611
- value: function _unloadCellValuesForQueryIds(queryIds) {
612
- if (this._isForceUnloaded) {
613
- return;
614
- }
516
+ key: "_loadCellValuesInFieldIdsAsync",
517
+ value: function _loadCellValuesInFieldIdsAsync(fieldIds) {
518
+ var _ref, newRecordsById, existingRecordsById, _iteratorNormalCompletion7, _didIteratorError7, _iteratorError7, _iterator7, _step7, _step7$value, recordId, newRecordObj, existingRecordObj, isCommentCountTypesSame, isCreatedTimeTypesSame, existingCellValuesByFieldId, i, fieldId, changedKeys;
519
+
520
+ return _regenerator.default.async(function _loadCellValuesInFieldIdsAsync$(_context3) {
521
+ while (1) {
522
+ switch (_context3.prev = _context3.next) {
523
+ case 0:
524
+ _context3.next = 2;
525
+ return _regenerator.default.awrap(this._airtableInterface.fetchAndSubscribeToCellValuesInFieldsAsync(this.tableId, fieldIds));
526
+
527
+ case 2:
528
+ _ref = _context3.sent;
529
+ newRecordsById = _ref.recordsById;
530
+
531
+ // Merge with existing data.
532
+ if (!this._data.recordsById) {
533
+ this._data.recordsById = {};
534
+ }
535
+
536
+ existingRecordsById = this._data.recordsById;
537
+ _iteratorNormalCompletion7 = true;
538
+ _didIteratorError7 = false;
539
+ _iteratorError7 = undefined;
540
+ _context3.prev = 9;
541
+
542
+ for (_iterator7 = (0, _private_utils.entries)((0, _private_utils.cast)(newRecordsById))[Symbol.iterator](); !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
543
+ _step7$value = (0, _slicedToArray2.default)(_step7.value, 2), recordId = _step7$value[0], newRecordObj = _step7$value[1];
544
+
545
+ if (!(0, _private_utils.has)(existingRecordsById, recordId)) {
546
+ existingRecordsById[recordId] = newRecordObj;
547
+ } else {
548
+ existingRecordObj = existingRecordsById[recordId]; // Metadata (createdTime, commentCount) should generally be up to date,
549
+ // but can be out of date in the rare scenario where realtime
550
+ // data has not yet been delivered to the SDK, but is populated in hyperbase
551
+ // at the time this new fetch is executed.
552
+ // istanbul ignore next
553
+
554
+ if (existingRecordObj.commentCount !== newRecordObj.commentCount) {
555
+ isCommentCountTypesSame = typeof existingRecordObj.commentCount !== typeof newRecordObj.commentCount;
556
+ (0, _error_utils.logErrorToSentry)('comment count out of sync - types are same: %s', {
557
+ isCommentCountTypesSame
558
+ });
559
+ } // istanbul ignore next
560
+
561
+
562
+ if (existingRecordObj.createdTime !== newRecordObj.createdTime) {
563
+ isCreatedTimeTypesSame = typeof existingRecordObj.createdTime !== typeof newRecordObj.createdTime;
564
+ (0, _error_utils.logErrorToSentry)('created time out of sync - types are same: %s', {
565
+ isCreatedTimeTypesSame
566
+ });
567
+ }
568
+
569
+ if (!existingRecordObj.cellValuesByFieldId) {
570
+ existingRecordObj.cellValuesByFieldId = {};
571
+ }
572
+
573
+ existingCellValuesByFieldId = existingRecordObj.cellValuesByFieldId;
574
+
575
+ for (i = 0; i < fieldIds.length; i++) {
576
+ fieldId = fieldIds[i];
577
+ existingCellValuesByFieldId[fieldId] = newRecordObj.cellValuesByFieldId ? newRecordObj.cellValuesByFieldId[fieldId] : undefined;
578
+ }
579
+ }
580
+ }
581
+
582
+ _context3.next = 17;
583
+ break;
584
+
585
+ case 13:
586
+ _context3.prev = 13;
587
+ _context3.t0 = _context3["catch"](9);
588
+ _didIteratorError7 = true;
589
+ _iteratorError7 = _context3.t0;
590
+
591
+ case 17:
592
+ _context3.prev = 17;
593
+ _context3.prev = 18;
594
+
595
+ if (!_iteratorNormalCompletion7 && _iterator7.return != null) {
596
+ _iterator7.return();
597
+ }
598
+
599
+ case 20:
600
+ _context3.prev = 20;
601
+
602
+ if (!_didIteratorError7) {
603
+ _context3.next = 23;
604
+ break;
605
+ }
606
+
607
+ throw _iteratorError7;
608
+
609
+ case 23:
610
+ return _context3.finish(20);
611
+
612
+ case 24:
613
+ return _context3.finish(17);
615
614
 
616
- this._unloadCellValuesForQueryIdsIgnoringForceUnloadedness(queryIds);
615
+ case 25:
616
+ changedKeys = fieldIds.map(fieldId => WatchableCellValuesInFieldKeyPrefix + fieldId); // Need to trigger onChange for records and recordIds since watching either
617
+ // of those causes record metadata to be loaded (via _getFieldIdForCausingRecordMetadataToLoad)
618
+ // and by convention we trigger a change event when data loads.
619
+
620
+ changedKeys.push(WatchableRecordStoreKeys.records);
621
+ changedKeys.push(WatchableRecordStoreKeys.recordIds); // Also trigger cellValues changes since the cell values in the fields
622
+ // are now loaded.
623
+
624
+ changedKeys.push(WatchableRecordStoreKeys.cellValues);
625
+ return _context3.abrupt("return", changedKeys);
626
+
627
+ case 30:
628
+ case "end":
629
+ return _context3.stop();
630
+ }
631
+ }
632
+ }, null, this, [[9, 13, 17, 25], [18,, 20, 24]]);
617
633
  }
618
634
  }, {
619
- key: "_unloadCellValuesForQueryIdsIgnoringForceUnloadedness",
620
- value: function _unloadCellValuesForQueryIdsIgnoringForceUnloadedness(queryIds) {
621
- this._queryManager.unsubscribeFromQueryIds(queryIds);
635
+ key: "unloadCellValuesInFieldIds",
636
+ value: function unloadCellValuesInFieldIds(fieldIds) {
637
+ if (this._isForceUnloaded) {
638
+ return;
639
+ }
622
640
 
623
- var _iteratorNormalCompletion7 = true;
624
- var _didIteratorError7 = false;
625
- var _iteratorError7 = undefined;
641
+ var fieldIdsWithZeroRetainCount = [];
642
+ var _iteratorNormalCompletion8 = true;
643
+ var _didIteratorError8 = false;
644
+ var _iteratorError8 = undefined;
626
645
 
627
646
  try {
628
- for (var _iterator7 = queryIds[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
629
- var queryId = _step7.value;
647
+ for (var _iterator8 = fieldIds[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
648
+ var fieldId = _step8.value;
649
+ var fieldRetainCount = this._cellValuesRetainCountByFieldId[fieldId] || 0;
650
+ fieldRetainCount--;
651
+
652
+ if (fieldRetainCount < 0) {
653
+ console.log('Field data over-released'); // eslint-disable-line no-console
630
654
 
631
- var queryIdWasLoaded = this._loadedQuerySpecIds.delete(queryId);
655
+ fieldRetainCount = 0;
656
+ }
657
+
658
+ this._cellValuesRetainCountByFieldId[fieldId] = fieldRetainCount;
632
659
 
633
- if (!queryIdWasLoaded) {
634
- (0, _warning.default)('Over unsubscribed from record store');
660
+ if (fieldRetainCount === 0) {
661
+ fieldIdsWithZeroRetainCount.push(fieldId);
635
662
  }
636
663
  }
637
664
  } catch (err) {
638
- _didIteratorError7 = true;
639
- _iteratorError7 = err;
665
+ _didIteratorError8 = true;
666
+ _iteratorError8 = err;
640
667
  } finally {
641
668
  try {
642
- if (!_iteratorNormalCompletion7 && _iterator7.return != null) {
643
- _iterator7.return();
669
+ if (!_iteratorNormalCompletion8 && _iterator8.return != null) {
670
+ _iterator8.return();
644
671
  }
645
672
  } finally {
646
- if (_didIteratorError7) {
647
- throw _iteratorError7;
673
+ if (_didIteratorError8) {
674
+ throw _iteratorError8;
648
675
  }
649
676
  }
650
677
  }
651
- }
652
- }, {
653
- key: "loadViewDataStoreAndCellValuesForFieldIdsAsync",
654
- value: function loadViewDataStoreAndCellValuesForFieldIdsAsync(viewId, fieldIdsToLoadOrNullIfAllFields) {
655
- var fieldSelection;
656
- return _regenerator.default.async(function loadViewDataStoreAndCellValuesForFieldIdsAsync$(_context3) {
657
- while (1) {
658
- switch (_context3.prev = _context3.next) {
659
- case 0:
660
- // We do not need to initialize a ViewDataStore, as it's a thin wrapper around the
661
- // hyperbase viewMetadataStore which can be initialized after a query inflates the data.
662
- // A complicating factor is what happens if (this) recordStore or viewDataStore are
663
- // forceUnload'ed. It is fine to track the query in only record store because:
664
- // - If either is force unloaded without the other the query must be kept active
665
- // - If a recordStore is force unloaded the children viewDataStores are also unloaded
666
- // because the table has been deleted. Therefore we can track it in only record_store.
667
- fieldSelection = fieldIdsToLoadOrNullIfAllFields ? {
668
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
669
- fieldIds: fieldIdsToLoadOrNullIfAllFields
670
- } : {
671
- type: _block_query_spec.BlockFieldSelectionSpecType.ALL_FIELDS_FROM_TABLE
672
- }; // By issuing this query we both populate cell values requested AND view metadata
673
- // Cell values requested will be accessible inside record_store
674
-
675
- _context3.next = 3;
676
- return _regenerator.default.awrap(this._loadCellValuesForQueryAsync({
677
- sourceType: _block_query_spec.BlockQuerySourceType.VIEW,
678
- sourceViewId: viewId,
679
- recordSelection: {
680
- fieldSelection
681
- },
682
- viewMetadataSelection: {
683
- shouldIncludeVisibleRecordIds: true,
684
- shouldIncludeFieldOrder: true,
685
- shouldIncludeGroups: true,
686
- shouldIncludeGroupingLevels: true,
687
- shouldIncludeColorsForRecordId: true
688
- }
689
- }));
690
678
 
691
- case 3:
692
- return _context3.abrupt("return", _context3.sent);
679
+ if (fieldIdsWithZeroRetainCount.length > 0) {
680
+ // Don't unload immediately. Wait a while in case something else
681
+ // requests the data, so we can avoid going back to liveapp or
682
+ // the network.
683
+ setTimeout(() => {
684
+ // Make sure the retain count is still zero, since it may
685
+ // have been incremented before the timeout fired.
686
+ var fieldIdsToUnload = fieldIdsWithZeroRetainCount.filter(fieldId => {
687
+ return this._cellValuesRetainCountByFieldId[fieldId] === 0;
688
+ });
689
+
690
+ if (fieldIdsToUnload.length > 0) {
691
+ // Set _areCellValuesLoadedByFieldId to false before calling _unloadCellValuesInFieldIds
692
+ // since _unloadCellValuesInFieldIds will check if *any* fields are still loaded.
693
+ var _iteratorNormalCompletion9 = true;
694
+ var _didIteratorError9 = false;
695
+ var _iteratorError9 = undefined;
693
696
 
694
- case 4:
695
- case "end":
696
- return _context3.stop();
697
+ try {
698
+ for (var _iterator9 = fieldIdsToUnload[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
699
+ var fieldId = _step9.value;
700
+ this._areCellValuesLoadedByFieldId[fieldId] = false;
701
+ }
702
+ } catch (err) {
703
+ _didIteratorError9 = true;
704
+ _iteratorError9 = err;
705
+ } finally {
706
+ try {
707
+ if (!_iteratorNormalCompletion9 && _iterator9.return != null) {
708
+ _iterator9.return();
709
+ }
710
+ } finally {
711
+ if (_didIteratorError9) {
712
+ throw _iteratorError9;
713
+ }
714
+ }
715
+ }
716
+
717
+ this._unloadCellValuesInFieldIds(fieldIdsToUnload);
697
718
  }
698
- }
699
- }, null, this);
719
+ }, _abstract_model_with_async_data.default.__DATA_UNLOAD_DELAY_MS);
720
+ }
700
721
  }
701
722
  }, {
702
- key: "unloadViewDataStoreAndCellValuesWithQueryIds",
703
- value: function unloadViewDataStoreAndCellValuesWithQueryIds(queryIds) {
704
- // Note that we do not update _numQueriesSubscribedToAllRecords since this query
705
- // does not subscribe to all cells in a table.
706
- this._unloadCellValuesForQueryIds(queryIds);
723
+ key: "_unloadCellValuesInFieldIds",
724
+ value: function _unloadCellValuesInFieldIds(fieldIds) {
725
+ this._airtableInterface.unsubscribeFromCellValuesInFields(this.tableId, fieldIds);
726
+
727
+ this._afterUnloadDataOrUnloadCellValuesInFieldIds(fieldIds);
707
728
  }
708
729
  }, {
709
- key: "loadAllCellValuesInFieldIdsAsync",
710
- value: function loadAllCellValuesInFieldIdsAsync(fieldIds) {
711
- var queryId;
712
- return _regenerator.default.async(function loadAllCellValuesInFieldIdsAsync$(_context4) {
730
+ key: "_loadDataAsync",
731
+ value: function _loadDataAsync() {
732
+ var tableData, changedKeys, _iteratorNormalCompletion10, _didIteratorError10, _iteratorError10, _iterator10, _step10, fieldId;
733
+
734
+ return _regenerator.default.async(function _loadDataAsync$(_context4) {
713
735
  while (1) {
714
736
  switch (_context4.prev = _context4.next) {
715
737
  case 0:
716
738
  _context4.next = 2;
717
- return _regenerator.default.awrap(this._loadCellValuesForQueryAsync({
718
- sourceType: _block_query_spec.BlockQuerySourceType.TABLE,
719
- recordSelection: {
720
- fieldSelection: {
721
- type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
722
- fieldIds
723
- }
724
- }
725
- }));
739
+ return _regenerator.default.awrap(this._airtableInterface.fetchAndSubscribeToTableDataAsync(this.tableId));
726
740
 
727
741
  case 2:
728
- queryId = _context4.sent;
729
- this._numQueriesSubscribedToAllRecords += 1;
730
- return _context4.abrupt("return", queryId);
742
+ tableData = _context4.sent;
743
+ this._data.recordsById = tableData.recordsById;
744
+ changedKeys = [WatchableRecordStoreKeys.records, WatchableRecordStoreKeys.recordIds, WatchableRecordStoreKeys.cellValues];
745
+ _iteratorNormalCompletion10 = true;
746
+ _didIteratorError10 = false;
747
+ _iteratorError10 = undefined;
748
+ _context4.prev = 8;
749
+
750
+ for (_iterator10 = (0, _private_utils.keys)(this._data.fieldsById)[Symbol.iterator](); !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
751
+ fieldId = _step10.value;
752
+ changedKeys.push(WatchableCellValuesInFieldKeyPrefix + fieldId);
753
+ }
754
+
755
+ _context4.next = 16;
756
+ break;
731
757
 
732
- case 5:
758
+ case 12:
759
+ _context4.prev = 12;
760
+ _context4.t0 = _context4["catch"](8);
761
+ _didIteratorError10 = true;
762
+ _iteratorError10 = _context4.t0;
763
+
764
+ case 16:
765
+ _context4.prev = 16;
766
+ _context4.prev = 17;
767
+
768
+ if (!_iteratorNormalCompletion10 && _iterator10.return != null) {
769
+ _iterator10.return();
770
+ }
771
+
772
+ case 19:
773
+ _context4.prev = 19;
774
+
775
+ if (!_didIteratorError10) {
776
+ _context4.next = 22;
777
+ break;
778
+ }
779
+
780
+ throw _iteratorError10;
781
+
782
+ case 22:
783
+ return _context4.finish(19);
784
+
785
+ case 23:
786
+ return _context4.finish(16);
787
+
788
+ case 24:
789
+ return _context4.abrupt("return", changedKeys);
790
+
791
+ case 25:
733
792
  case "end":
734
793
  return _context4.stop();
735
794
  }
736
795
  }
737
- }, null, this);
796
+ }, null, this, [[8, 12, 16, 24], [17,, 19, 23]]);
738
797
  }
739
798
  }, {
740
- key: "loadAllCellValuesAsync",
741
- value: function loadAllCellValuesAsync() {
742
- var queryId;
743
- return _regenerator.default.async(function loadAllCellValuesAsync$(_context5) {
744
- while (1) {
745
- switch (_context5.prev = _context5.next) {
746
- case 0:
747
- _context5.next = 2;
748
- return _regenerator.default.awrap(this._loadCellValuesForQueryAsync({
749
- sourceType: _block_query_spec.BlockQuerySourceType.TABLE,
750
- recordSelection: {
751
- fieldSelection: {
752
- type: _block_query_spec.BlockFieldSelectionSpecType.ALL_FIELDS_FROM_TABLE
753
- }
754
- }
755
- }));
799
+ key: "_unloadData",
800
+ value: function _unloadData() {
801
+ this._airtableInterface.unsubscribeFromTableData(this.tableId);
756
802
 
757
- case 2:
758
- queryId = _context5.sent;
759
- this._numQueriesSubscribedToAllRecords += 1;
760
- return _context5.abrupt("return", queryId);
803
+ this._afterUnloadDataOrUnloadCellValuesInFieldIds();
804
+ }
805
+ }, {
806
+ key: "_afterUnloadDataOrUnloadCellValuesInFieldIds",
807
+ value: function _afterUnloadDataOrUnloadCellValuesInFieldIds(unloadedFieldIds) {
808
+ var areAnyFieldsLoaded = this.isDataLoaded || (0, _private_utils.values)(this._areCellValuesLoadedByFieldId).some(isLoaded => isLoaded);
809
+
810
+ if (!this.isDeleted) {
811
+ if (!areAnyFieldsLoaded) {
812
+ this._data.recordsById = undefined;
813
+ } else if (!this.isDataLoaded) {
814
+ var fieldIdsToClear;
815
+
816
+ if (unloadedFieldIds) {
817
+ // Specific fields were unloaded, so clear out the cell values for those fields.
818
+ fieldIdsToClear = unloadedFieldIds;
819
+ } else {
820
+ // The entire table was unloaded, but some individual fields are still loaded.
821
+ // We need to clear out the cell values of every field that was unloaded.
822
+ // This is kind of slow, but hopefully uncommon.
823
+ var fieldIds = Object.keys(this._data.fieldsById);
824
+ fieldIdsToClear = fieldIds.filter(fieldId => !this._areCellValuesLoadedByFieldId[fieldId]);
825
+ }
761
826
 
762
- case 5:
763
- case "end":
764
- return _context5.stop();
827
+ var recordsById = this._data.recordsById;
828
+ var _iteratorNormalCompletion11 = true;
829
+ var _didIteratorError11 = false;
830
+ var _iteratorError11 = undefined;
831
+
832
+ try {
833
+ for (var _iterator11 = (0, _private_utils.values)(recordsById || {})[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
834
+ var recordObj = _step11.value;
835
+
836
+ for (var i = 0; i < fieldIdsToClear.length; i++) {
837
+ var fieldId = fieldIdsToClear[i];
838
+
839
+ if (recordObj.cellValuesByFieldId) {
840
+ recordObj.cellValuesByFieldId[fieldId] = undefined;
841
+ }
842
+ }
843
+ }
844
+ } catch (err) {
845
+ _didIteratorError11 = true;
846
+ _iteratorError11 = err;
847
+ } finally {
848
+ try {
849
+ if (!_iteratorNormalCompletion11 && _iterator11.return != null) {
850
+ _iterator11.return();
851
+ }
852
+ } finally {
853
+ if (_didIteratorError11) {
854
+ throw _iteratorError11;
855
+ }
856
+ }
765
857
  }
766
858
  }
767
- }, null, this);
859
+ }
860
+
861
+ if (!areAnyFieldsLoaded) {
862
+ this._recordModelsById = {};
863
+ }
768
864
  }
769
865
  }, {
770
- key: "unloadCellValuesForQueryIdsThatAreSubscribedToAllCells",
771
- value: function unloadCellValuesForQueryIdsThatAreSubscribedToAllCells(queryIds) {
772
- this._unloadCellValuesForQueryIds(queryIds);
866
+ key: "triggerOnChangeForDirtyPaths",
867
+ value: function triggerOnChangeForDirtyPaths(dirtyPaths) {
868
+ if (this.isRecordMetadataLoaded && dirtyPaths.recordsById) {
869
+ // Since tables don't have a record order, need to detect if a record
870
+ // was created or deleted and trigger onChange for records.
871
+ var dirtyFieldIdsSet = {};
872
+ var addedRecordIds = [];
873
+ var removedRecordIds = [];
874
+ var _iteratorNormalCompletion12 = true;
875
+ var _didIteratorError12 = false;
876
+ var _iteratorError12 = undefined;
773
877
 
774
- this._numQueriesSubscribedToAllRecords -= queryIds.length;
775
- } // Maybe add a comment "pending removal of inheriting from parent class AbstractModelWithAsyncData"
776
- // istanbul ignore next
878
+ try {
879
+ for (var _iterator12 = (0, _private_utils.entries)(dirtyPaths.recordsById)[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) {
880
+ var _step12$value = (0, _slicedToArray2.default)(_step12.value, 2),
881
+ recordId = _step12$value[0],
882
+ dirtyRecordPaths = _step12$value[1];
883
+
884
+ if (dirtyRecordPaths && dirtyRecordPaths._isDirty) {
885
+ // If the entire record is dirty, it was either created or deleted.
886
+ (0, _error_utils.invariant)(this._data.recordsById, 'No recordsById');
887
+
888
+ if ((0, _private_utils.has)(this._data.recordsById, recordId)) {
889
+ addedRecordIds.push(recordId);
890
+ } else {
891
+ removedRecordIds.push(recordId);
892
+ var recordModel = this._recordModelsById[recordId];
893
+
894
+ if (recordModel) {
895
+ // Remove the Record model if it was deleted.
896
+ delete this._recordModelsById[recordId];
897
+ }
898
+ }
899
+ } else {
900
+ var _recordModel = this._recordModelsById[recordId];
777
901
 
778
- }, {
779
- key: "_loadDataAsync",
780
- value: function _loadDataAsync() {
781
- return _regenerator.default.async(function _loadDataAsync$(_context6) {
782
- while (1) {
783
- switch (_context6.prev = _context6.next) {
784
- case 0:
785
- throw (0, _error_utils.spawnError)('Internal error - SDK called loadData on record_store');
902
+ if (_recordModel) {
903
+ _recordModel.__triggerOnChangeForDirtyPaths(dirtyRecordPaths);
904
+ }
905
+ }
786
906
 
787
- case 1:
788
- case "end":
789
- return _context6.stop();
907
+ var cellValuesByFieldId = dirtyRecordPaths.cellValuesByFieldId;
908
+
909
+ if (cellValuesByFieldId) {
910
+ for (var _i2 = 0, _Object$keys2 = Object.keys(cellValuesByFieldId); _i2 < _Object$keys2.length; _i2++) {
911
+ var fieldId = _Object$keys2[_i2];
912
+ dirtyFieldIdsSet[fieldId] = true;
913
+ }
914
+ }
915
+ } // Now that we've composed our created/deleted record ids arrays, let's fire
916
+ // the records onChange event if any records were created or deleted.
917
+
918
+ } catch (err) {
919
+ _didIteratorError12 = true;
920
+ _iteratorError12 = err;
921
+ } finally {
922
+ try {
923
+ if (!_iteratorNormalCompletion12 && _iterator12.return != null) {
924
+ _iterator12.return();
925
+ }
926
+ } finally {
927
+ if (_didIteratorError12) {
928
+ throw _iteratorError12;
929
+ }
790
930
  }
791
931
  }
792
- });
793
- } // Maybe add a comment "pending removal of inheriting from parent class AbstractModelWithAsyncData"
794
- // istanbul ignore next
795
932
 
796
- }, {
797
- key: "_unloadData",
798
- value: function _unloadData() {
799
- throw (0, _error_utils.spawnError)('Internal error - SDK called unloadData on record_store');
933
+ if (addedRecordIds.length > 0 || removedRecordIds.length > 0) {
934
+ this._onChange(WatchableRecordStoreKeys.records, {
935
+ addedRecordIds,
936
+ removedRecordIds
937
+ });
938
+
939
+ this._onChange(WatchableRecordStoreKeys.recordIds, {
940
+ addedRecordIds,
941
+ removedRecordIds
942
+ });
943
+ } // NOTE: this is an experimental (and somewhat messy) way to watch
944
+ // for changes to cells in a table, as an alternative to implementing
945
+ // full event bubbling. For now, it unblocks the things we want to
946
+ // build, but we may replace it.
947
+ // If we keep it, could be more efficient by not calling _onChange
948
+ // if there are no subscribers.
949
+ // TODO: don't trigger changes for fields that aren't supposed to be loaded
950
+ // (in some cases, e.g. record created, liveapp will send cell values
951
+ // that we're not subscribed to).
952
+
953
+
954
+ var fieldIds = Object.freeze(Object.keys(dirtyFieldIdsSet));
955
+ var recordIds = Object.freeze(Object.keys(dirtyPaths.recordsById));
956
+
957
+ if (fieldIds.length > 0 && recordIds.length > 0) {
958
+ this._onChange(WatchableRecordStoreKeys.cellValues, {
959
+ recordIds,
960
+ fieldIds
961
+ });
962
+ }
963
+
964
+ var _iteratorNormalCompletion13 = true;
965
+ var _didIteratorError13 = false;
966
+ var _iteratorError13 = undefined;
967
+
968
+ try {
969
+ for (var _iterator13 = fieldIds[Symbol.iterator](), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) {
970
+ var _fieldId2 = _step13.value;
971
+
972
+ this._onChange(WatchableCellValuesInFieldKeyPrefix + _fieldId2, recordIds, _fieldId2);
973
+ }
974
+ } catch (err) {
975
+ _didIteratorError13 = true;
976
+ _iteratorError13 = err;
977
+ } finally {
978
+ try {
979
+ if (!_iteratorNormalCompletion13 && _iterator13.return != null) {
980
+ _iterator13.return();
981
+ }
982
+ } finally {
983
+ if (_didIteratorError13) {
984
+ throw _iteratorError13;
985
+ }
986
+ }
987
+ }
988
+ }
989
+
990
+ if (dirtyPaths.viewOrder) {
991
+ // clean up deleted views
992
+ var _iteratorNormalCompletion14 = true;
993
+ var _didIteratorError14 = false;
994
+ var _iteratorError14 = undefined;
995
+
996
+ try {
997
+ for (var _iterator14 = (0, _private_utils.entries)(this._viewDataStoresByViewId)[Symbol.iterator](), _step14; !(_iteratorNormalCompletion14 = (_step14 = _iterator14.next()).done); _iteratorNormalCompletion14 = true) {
998
+ var _step14$value = (0, _slicedToArray2.default)(_step14.value, 2),
999
+ viewId = _step14$value[0],
1000
+ viewDataStore = _step14$value[1];
1001
+
1002
+ if (viewDataStore.isDeleted) {
1003
+ viewDataStore.__onDataDeletion();
1004
+
1005
+ delete this._viewDataStoresByViewId[viewId];
1006
+ }
1007
+ }
1008
+ } catch (err) {
1009
+ _didIteratorError14 = true;
1010
+ _iteratorError14 = err;
1011
+ } finally {
1012
+ try {
1013
+ if (!_iteratorNormalCompletion14 && _iterator14.return != null) {
1014
+ _iterator14.return();
1015
+ }
1016
+ } finally {
1017
+ if (_didIteratorError14) {
1018
+ throw _iteratorError14;
1019
+ }
1020
+ }
1021
+ }
1022
+ }
800
1023
  }
801
1024
  }, {
802
1025
  key: "_dataOrNullIfDeleted",
@@ -808,7 +1031,9 @@ function (_AbstractModelWithAsy) {
808
1031
  }, {
809
1032
  key: "records",
810
1033
  get: function get() {
811
- var records = this.recordIds.map(recordId => {
1034
+ var recordsById = this._data.recordsById;
1035
+ (0, _error_utils.invariant)(recordsById, 'Record metadata is not loaded');
1036
+ var records = Object.keys(recordsById).map(recordId => {
812
1037
  var record = this.getRecordByIdIfExists(recordId);
813
1038
  (0, _error_utils.invariant)(record, 'record');
814
1039
  return record;
@@ -823,17 +1048,14 @@ function (_AbstractModelWithAsy) {
823
1048
  }, {
824
1049
  key: "recordIds",
825
1050
  get: function get() {
826
- (0, _error_utils.invariant)(this.isSubscribedToAllRecordsInTable, 'Record metadata is not loaded for all records in table');
827
-
828
- var recordIds = this._getRecordStoreForTable().getPartiallyLoadedRecordIds();
829
-
830
- (0, _error_utils.invariant)(recordIds, 'Record metadata is not loaded');
831
- return recordIds;
1051
+ var recordsById = this._data.recordsById;
1052
+ (0, _error_utils.invariant)(recordsById, 'Record metadata is not loaded');
1053
+ return Object.keys(recordsById);
832
1054
  }
833
1055
  }, {
834
- key: "isSubscribedToAllRecordsInTable",
1056
+ key: "isRecordMetadataLoaded",
835
1057
  get: function get() {
836
- return this._numQueriesSubscribedToAllRecords > 0;
1058
+ return !!this._data.recordsById;
837
1059
  }
838
1060
  }]);
839
1061
  return RecordStore;