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