@airtable/blocks 1.10.1 → 1.10.2-experimental-640bd10-20220211

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 (74) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/dist/cjs/error_utils.js +44 -2
  3. package/dist/cjs/models/grouped_record_query_result.js +5 -14
  4. package/dist/cjs/models/linked_records_query_result.js +78 -27
  5. package/dist/cjs/models/mutations.js +18 -162
  6. package/dist/cjs/models/query_manager.js +328 -0
  7. package/dist/cjs/models/record.js +310 -55
  8. package/dist/cjs/models/record_query_result.js +4 -1
  9. package/dist/cjs/models/record_store.js +557 -765
  10. package/dist/cjs/models/table.js +6 -6
  11. package/dist/cjs/models/table_or_view_query_result.js +526 -419
  12. package/dist/cjs/models/view_data_store.js +255 -295
  13. package/dist/cjs/private_utils.js +40 -0
  14. package/dist/cjs/sdk.js +12 -2
  15. package/dist/cjs/testing/abstract_mock_airtable_interface.js +57 -11
  16. package/dist/cjs/testing/fixture_data.js +268 -0
  17. package/dist/cjs/testing/mock_base_data_stores.js +876 -0
  18. package/dist/cjs/types/airtable_interface.js +17 -2
  19. package/dist/cjs/types/block_query_spec.js +85 -0
  20. package/dist/cjs/ui/use_records.js +5 -1
  21. package/dist/cjs/unstable_testing_utils.js +55 -1
  22. package/dist/cjs/watchable.js +15 -1
  23. package/dist/types/src/error_utils.d.ts +6 -0
  24. package/dist/types/src/error_utils.d.ts.map +1 -1
  25. package/dist/types/src/models/grouped_record_query_result.d.ts +3 -3
  26. package/dist/types/src/models/grouped_record_query_result.d.ts.map +1 -1
  27. package/dist/types/src/models/linked_records_query_result.d.ts.map +1 -1
  28. package/dist/types/src/models/mutations.d.ts.map +1 -1
  29. package/dist/types/src/models/query_manager.d.ts +2 -0
  30. package/dist/types/src/models/query_manager.d.ts.map +1 -0
  31. package/dist/types/src/models/record.d.ts +12 -3
  32. package/dist/types/src/models/record.d.ts.map +1 -1
  33. package/dist/types/src/models/record_query_result.d.ts +3 -2
  34. package/dist/types/src/models/record_query_result.d.ts.map +1 -1
  35. package/dist/types/src/models/record_store.d.ts.map +1 -1
  36. package/dist/types/src/models/table_or_view_query_result.d.ts +3 -5
  37. package/dist/types/src/models/table_or_view_query_result.d.ts.map +1 -1
  38. package/dist/types/src/models/view_data_store.d.ts +0 -1
  39. package/dist/types/src/models/view_data_store.d.ts.map +1 -1
  40. package/dist/types/src/models/view_metadata_query_result.d.ts +1 -1
  41. package/dist/types/src/models/view_metadata_query_result.d.ts.map +1 -1
  42. package/dist/types/src/private_utils.d.ts +24 -1
  43. package/dist/types/src/private_utils.d.ts.map +1 -1
  44. package/dist/types/src/sdk.d.ts.map +1 -1
  45. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts +11 -11
  46. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts.map +1 -1
  47. package/dist/types/{test/airtable_interface_mocks → src/testing}/fixture_data.d.ts +42 -19
  48. package/dist/types/src/testing/fixture_data.d.ts.map +1 -0
  49. package/dist/types/src/testing/mock_base_data_stores.d.ts +55 -0
  50. package/dist/types/src/testing/mock_base_data_stores.d.ts.map +1 -0
  51. package/dist/types/src/types/airtable_interface.d.ts +59 -20
  52. package/dist/types/src/types/airtable_interface.d.ts.map +1 -1
  53. package/dist/types/src/types/block_query_spec.d.ts +139 -0
  54. package/dist/types/src/types/block_query_spec.d.ts.map +1 -0
  55. package/dist/types/src/types/table.d.ts +0 -2
  56. package/dist/types/src/types/table.d.ts.map +1 -1
  57. package/dist/types/src/types/view.d.ts +3 -8
  58. package/dist/types/src/types/view.d.ts.map +1 -1
  59. package/dist/types/src/ui/record_card.d.ts +1 -1
  60. package/dist/types/src/unstable_testing_utils.d.ts +4 -1
  61. package/dist/types/src/unstable_testing_utils.d.ts.map +1 -1
  62. package/dist/types/src/watchable.d.ts.map +1 -1
  63. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts +1 -1
  64. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts.map +1 -1
  65. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts +19 -12
  66. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts.map +1 -1
  67. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts +1 -1
  68. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts.map +1 -1
  69. package/dist/types/test/test_helpers.d.ts +2 -0
  70. package/dist/types/test/test_helpers.d.ts.map +1 -1
  71. package/dist/types/test/testing/fixture_data.test.d.ts +2 -0
  72. package/dist/types/test/testing/fixture_data.test.d.ts.map +1 -0
  73. package/package.json +2 -1
  74. package/dist/types/test/airtable_interface_mocks/fixture_data.d.ts.map +0 -1
@@ -6,12 +6,20 @@ 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
+ require("core-js/modules/es.array.filter");
12
+
13
+ require("core-js/modules/es.array.includes");
14
+
9
15
  require("core-js/modules/es.array.iterator");
10
16
 
11
17
  require("core-js/modules/es.object.to-string");
12
18
 
13
19
  require("core-js/modules/es.promise");
14
20
 
21
+ require("core-js/modules/es.string.includes");
22
+
15
23
  require("core-js/modules/es.string.starts-with");
16
24
 
17
25
  require("core-js/modules/web.dom-collections.iterator");
@@ -29,9 +37,11 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
29
37
 
30
38
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
31
39
 
40
+ var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
41
+
32
42
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
33
43
 
34
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
44
+ var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
35
45
 
36
46
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
37
47
 
@@ -47,6 +57,10 @@ var _error_utils = require("../error_utils");
47
57
 
48
58
  var _color_utils = _interopRequireDefault(require("../color_utils"));
49
59
 
60
+ var _block_query_spec = require("../types/block_query_spec");
61
+
62
+ var _airtable_interface = require("../types/airtable_interface");
63
+
50
64
  var _abstract_model = _interopRequireDefault(require("./abstract_model"));
51
65
 
52
66
  var _object_pool = _interopRequireDefault(require("./object_pool"));
@@ -80,6 +94,23 @@ var WatchableColorInViewKeyPrefix = 'colorInView:';
80
94
  * - `'colorInView:' + someViewId`
81
95
  */
82
96
 
97
+ function _isKeySubscribeableViaRecordQuery(key) {
98
+ switch (key) {
99
+ case WatchableRecordKeys.name:
100
+ case WatchableRecordKeys.commentCount:
101
+ case WatchableRecordKeys.cellValues:
102
+ return true;
103
+
104
+ default:
105
+ if (key.startsWith(WatchableCellValueInFieldKeyPrefix)) {
106
+ return true;
107
+ }
108
+
109
+ break;
110
+ }
111
+
112
+ return false;
113
+ }
83
114
  /**
84
115
  * Model class representing a record in a table.
85
116
  *
@@ -88,6 +119,8 @@ var WatchableColorInViewKeyPrefix = 'colorInView:';
88
119
  *
89
120
  * @docsPath models/Record
90
121
  */
122
+
123
+
91
124
  var Record =
92
125
  /*#__PURE__*/
93
126
  function (_AbstractModel) {
@@ -115,9 +148,16 @@ function (_AbstractModel) {
115
148
  _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(Record).call(this, sdk, recordId));
116
149
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_parentRecordStore", void 0);
117
150
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_parentTable", void 0);
151
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_queryManager", void 0);
118
152
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "__linkedRecordsQueryResultPool", void 0);
153
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_onRecordColorChange", (viewDataStore, key, recordIds) => {
154
+ if (recordIds.includes(_this.id)) {
155
+ _this._onChange(WatchableColorInViewKeyPrefix + viewDataStore.viewId);
156
+ }
157
+ });
119
158
  _this._parentRecordStore = parentRecordStore;
120
159
  _this._parentTable = parentTable;
160
+ _this._queryManager = sdk._queryManager;
121
161
  _this.__linkedRecordsQueryResultPool = new _object_pool.default(_linked_records_query_result.default);
122
162
  return _this;
123
163
  }
@@ -127,6 +167,33 @@ function (_AbstractModel) {
127
167
 
128
168
 
129
169
  (0, _createClass2.default)(Record, [{
170
+ key: "_assertExistsAndLoaded",
171
+
172
+ /**
173
+ * @internal
174
+ */
175
+ value: function _assertExistsAndLoaded() {
176
+ // Access data in order to throw if the Record has been deleted / isn't loaded
177
+ var data = this._dataOrNullIfDeleted;
178
+
179
+ if (data === null) {
180
+ throw this._spawnErrorForDeletion();
181
+ }
182
+ }
183
+ /**
184
+ * The table that this record belongs to. Should never change because records aren't moved between tables.
185
+ *
186
+ * @internal (since we may not be able to return parent model instances in the immutable models world)
187
+ * @example
188
+ * ```js
189
+ * import {useRecords} from '@airtable/blocks/ui';
190
+ * const records = useRecords(myTable);
191
+ * console.log(records[0].parentTable.id === myTable.id);
192
+ * // => true
193
+ * ```
194
+ */
195
+
196
+ }, {
130
197
  key: "_getFieldMatching",
131
198
 
132
199
  /**
@@ -156,18 +223,20 @@ function (_AbstractModel) {
156
223
  }, {
157
224
  key: "_getRawCellValue",
158
225
  value: function _getRawCellValue(field) {
159
- (0, _error_utils.invariant)(this._parentRecordStore.areCellValuesLoadedForFieldId(field.id), 'Cell values for field %s are not loaded', field.id);
160
- var cellValuesByFieldId = this._data.cellValuesByFieldId;
226
+ this._assertExistsAndLoaded(); // TODO: (#proj-blocks-sdk-record-limits) Consider a new API that allows determining
227
+ // if the cell value is loaded + getting the cellValue in one call?
161
228
 
162
- if (!cellValuesByFieldId) {
163
- return null;
164
- }
165
229
 
166
- var cellValue = cellValuesByFieldId[field.id] !== undefined ? cellValuesByFieldId[field.id] : null;
230
+ var isCellValueLoadedForField = this._parentRecordStore.isRecordCellValueLoadedForFieldId(this._id, field.id);
231
+
232
+ (0, _error_utils.invariant)(isCellValueLoadedForField, 'Cell value for field %s and record %s is not loaded', field.id, this._id);
233
+
234
+ var cellValue = this._parentRecordStore.getRecordCellValueByFieldId(this._id, field.id);
167
235
 
168
236
  if (typeof cellValue === 'object' && cellValue !== null) {
169
237
  // Copy non-primitives.
170
- // TODO(kasra): maybe freezeDeep instead?
238
+ // TODO: (#proj-blocks-sdk-record-limits): getRecordCellValueByFieldId returns
239
+ // deeply frozen objects, in a later sdk version we can drop cloning deeply.
171
240
  return (0, _private_utils.cloneDeep)(cellValue);
172
241
  } else {
173
242
  return cellValue;
@@ -278,9 +347,8 @@ function (_AbstractModel) {
278
347
  }, {
279
348
  key: "getCellValueAsString",
280
349
  value: function getCellValueAsString(fieldOrFieldIdOrFieldName) {
281
- var field = this._getFieldMatching(fieldOrFieldIdOrFieldName);
350
+ var field = this._getFieldMatching(fieldOrFieldIdOrFieldName); // getRawCellValue performs invariant check that cell value at the field is loaded
282
351
 
283
- (0, _error_utils.invariant)(this._parentRecordStore.areCellValuesLoadedForFieldId(field.id), 'Cell values for field %s are not loaded', field.id);
284
352
 
285
353
  var cellValue = this._getRawCellValue(field);
286
354
 
@@ -443,73 +511,256 @@ function (_AbstractModel) {
443
511
  */
444
512
 
445
513
  }, {
446
- key: "__triggerOnChangeForDirtyPaths",
514
+ key: "_constructBlockFieldSelectionSpec",
447
515
 
448
516
  /**
449
517
  * @internal
450
518
  */
451
- value: function __triggerOnChangeForDirtyPaths(dirtyPaths) {
452
- var cellValuesByFieldId = dirtyPaths.cellValuesByFieldId,
453
- commentCount = dirtyPaths.commentCount;
454
-
455
- if (cellValuesByFieldId && !(0, _private_utils.isObjectEmpty)(cellValuesByFieldId)) {
456
- // TODO: don't trigger changes for fields that aren't supposed to be loaded
457
- // (in some cases, e.g. record created, liveapp will send cell values
458
- // that we're not subscribed to).
459
- this._onChange(WatchableRecordKeys.cellValues, Object.keys(cellValuesByFieldId));
460
-
461
- if (cellValuesByFieldId[this.parentTable.primaryField.id]) {
462
- this._onChange(WatchableRecordKeys.name);
519
+ value: function _constructBlockFieldSelectionSpec(fieldSelection) {
520
+ return {
521
+ sourceType: _block_query_spec.BlockQuerySourceType.TABLE,
522
+ sourceTableId: this.parentTable.id,
523
+ recordSelection: {
524
+ recordIds: [this.id],
525
+ fieldSelection
463
526
  }
527
+ };
528
+ }
529
+ /**
530
+ * @internal
531
+ */
464
532
 
465
- for (var _i = 0, _Object$keys = Object.keys(cellValuesByFieldId); _i < _Object$keys.length; _i++) {
466
- var fieldId = _Object$keys[_i];
533
+ }, {
534
+ key: "_findMatchingBlockSubscriptionChanges",
535
+ value: function _findMatchingBlockSubscriptionChanges(changes, changeTypeToMatch) {
536
+ return changes.filter(change => change.type === changeTypeToMatch && change.tableId === this.parentTable.id);
537
+ }
538
+ /**
539
+ * @internal
540
+ */
541
+
542
+ }, {
543
+ key: "_constructBlockQuerySpecAndCallbackForWatchableKey",
544
+ value: function _constructBlockQuerySpecAndCallbackForWatchableKey(key, callbackForRegistration, context) {
545
+ var fieldSelectionDefinition;
546
+
547
+ var onChangeCallback = () => {
548
+ callbackForRegistration.call(context, this, key);
549
+ };
550
+
551
+ switch (key) {
552
+ case WatchableRecordKeys.name:
553
+ fieldSelectionDefinition = {
554
+ type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
555
+ fieldIds: [this.parentTable.primaryField.id]
556
+ };
557
+ break;
558
+
559
+ case WatchableRecordKeys.commentCount:
560
+ fieldSelectionDefinition = {
561
+ type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
562
+ fieldIds: [],
563
+ shouldIncludeCommentCount: true
564
+ };
565
+ break;
566
+
567
+ case WatchableRecordKeys.cellValues:
568
+ // TODO: (#proj-blocks-sdk-record-limits) In the future don't fetch all fields from table
569
+ // old code exhibit this behavior though: https://github.com/Hyperbase/blocks-sdk/blob/aba6fa7b7f8ccc1e364b409ca466d0281f8e9a69/packages/sdk/src/models/record.ts#L414-L416
570
+ // In the future a "subscribe to updates, but don't fetch more data" might be useful here.
571
+ fieldSelectionDefinition = {
572
+ type: _block_query_spec.BlockFieldSelectionSpecType.ALL_FIELDS_FROM_TABLE
573
+ };
574
+
575
+ onChangeCallback = changes => {
576
+ var modifiedCellValuesChanges = this._findMatchingBlockSubscriptionChanges(changes, _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE); // We do not currently simulate "on load" callback for registration
577
+ // but still handle it.
578
+
579
+
580
+ if (modifiedCellValuesChanges.length === 0) {
581
+ callbackForRegistration.call(context, this, key);
582
+ return;
583
+ }
584
+
585
+ var fieldIdsModified = [];
586
+ var _iteratorNormalCompletion3 = true;
587
+ var _didIteratorError3 = false;
588
+ var _iteratorError3 = undefined;
589
+
590
+ try {
591
+ for (var _iterator3 = modifiedCellValuesChanges[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
592
+ var modifiedCellValuesChange = _step3.value;
593
+ (0, _error_utils.invariant)(modifiedCellValuesChange.type === _block_query_spec.BlockQuerySubscriptionChangeType.MODIFIED_CELL_VALUES_FOR_TABLE, 'Modified cell value change is not a supported type in record');
594
+ var fieldIdsByRecordId = modifiedCellValuesChange.fieldIdsByRecordId;
595
+ var fieldIdsForThisRecord = fieldIdsByRecordId[this.id];
596
+
597
+ if (fieldIdsForThisRecord) {
598
+ fieldIdsModified = fieldIdsModified.concat(fieldIdsForThisRecord);
599
+ }
600
+ }
601
+ } catch (err) {
602
+ _didIteratorError3 = true;
603
+ _iteratorError3 = err;
604
+ } finally {
605
+ try {
606
+ if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
607
+ _iterator3.return();
608
+ }
609
+ } finally {
610
+ if (_didIteratorError3) {
611
+ throw _iteratorError3;
612
+ }
613
+ }
614
+ }
615
+
616
+ callbackForRegistration.call(context, this, key, fieldIdsModified);
617
+ };
618
+
619
+ break;
620
+
621
+ default:
622
+ // The else cases should be impossible to reach - they indicate a SDK error.
623
+ // istanbul ignore else
624
+ if (key.startsWith(WatchableCellValueInFieldKeyPrefix)) {
625
+ var fieldId = key.substring(WatchableCellValueInFieldKeyPrefix.length); // Assert that fieldId is validly formatted, not that it is a valid fieldId in this table
626
+
627
+ (0, _error_utils.invariant)((0, _private_utils.isFieldId)(fieldId), 'fieldId passed as watchable fieldId is not a valid format' + fieldId);
628
+ fieldSelectionDefinition = {
629
+ type: _block_query_spec.BlockFieldSelectionSpecType.SPECIFIED_FIELDS,
630
+ fieldIds: [fieldId]
631
+ };
632
+
633
+ onChangeCallback = changes => {
634
+ // We could check if something else changed in the changes array,
635
+ // but our query subscription is targeting only the field in this record
636
+ callbackForRegistration.call(context, this, key, fieldId);
637
+ };
638
+ } else if (key.startsWith(WatchableColorInViewKeyPrefix)) {
639
+ throw (0, _error_utils.spawnError)('WatchableColorInViewKeyPrefix must be handled outside of query system');
640
+ } else {
641
+ throw (0, _error_utils.spawnError)('invalid record (un)watch key %s', key);
642
+ }
467
643
 
468
- this._onChange(WatchableCellValueInFieldKeyPrefix + fieldId, fieldId);
469
- }
470
644
  }
471
645
 
472
- if (commentCount) {
473
- this._onChange(WatchableRecordKeys.commentCount);
646
+ return {
647
+ querySpecToSubscribeWith: this._constructBlockFieldSelectionSpec(fieldSelectionDefinition),
648
+ onChangeCallback
649
+ };
650
+ }
651
+ /** @inheritdoc */
652
+
653
+ }, {
654
+ key: "watch",
655
+ value: function watch(keys, callbackForRegistration, context) {
656
+ var validKeys = this._getWatchableValidKeysOrThrow(keys, 'watch');
657
+
658
+ var _iteratorNormalCompletion4 = true;
659
+ var _didIteratorError4 = false;
660
+ var _iteratorError4 = undefined;
661
+
662
+ try {
663
+ for (var _iterator4 = validKeys[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
664
+ var key = _step4.value;
665
+
666
+ if (_isKeySubscribeableViaRecordQuery(key)) {
667
+ var _this$_constructBlock = this._constructBlockQuerySpecAndCallbackForWatchableKey(key, callbackForRegistration, context),
668
+ querySpecToSubscribeWith = _this$_constructBlock.querySpecToSubscribeWith,
669
+ onChangeCallback = _this$_constructBlock.onChangeCallback;
670
+
671
+ this._queryManager.watchWithQuerySpec(_airtable_interface.BlockQueryCallbackPriority.BLOCK_CODE, querySpecToSubscribeWith, this, key, onChangeCallback, callbackForRegistration, context);
672
+ } else {
673
+ // fallback to regular watch logic
674
+ (0, _error_utils.invariant)(key.startsWith(WatchableColorInViewKeyPrefix), 'Unexpected key in watch: %s', key);
675
+ var viewId = key.substring(WatchableColorInViewKeyPrefix.length);
676
+
677
+ this._parentRecordStore.getViewDataStore(viewId).watch('recordColors', this._onRecordColorChange);
678
+
679
+ (0, _get2.default)((0, _getPrototypeOf2.default)(Record.prototype), "watch", this).call(this, key, callbackForRegistration, context);
680
+ }
681
+ }
682
+ } catch (err) {
683
+ _didIteratorError4 = true;
684
+ _iteratorError4 = err;
685
+ } finally {
686
+ try {
687
+ if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
688
+ _iterator4.return();
689
+ }
690
+ } finally {
691
+ if (_didIteratorError4) {
692
+ throw _iteratorError4;
693
+ }
694
+ }
474
695
  }
696
+
697
+ return validKeys;
475
698
  }
476
- /**
477
- * @internal
478
- */
699
+ /** @inheritdoc */
479
700
 
480
701
  }, {
481
- key: "__triggerOnChangeForRecordColorInViewId",
482
- value: function __triggerOnChangeForRecordColorInViewId(viewId) {
483
- this._onChange(WatchableColorInViewKeyPrefix + viewId);
702
+ key: "unwatch",
703
+ value: function unwatch(keys, callbackForRegistration, context) {
704
+ // We warn instead of throw here because we used to warn instead of throw and don't
705
+ // want to make a breaking change.
706
+ var validKeys = this._getWatchableValidKeysOrThrow(keys, 'unwatch', true);
707
+
708
+ var _iteratorNormalCompletion5 = true;
709
+ var _didIteratorError5 = false;
710
+ var _iteratorError5 = undefined;
711
+
712
+ try {
713
+ for (var _iterator5 = validKeys[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
714
+ var key = _step5.value;
715
+
716
+ if (_isKeySubscribeableViaRecordQuery(key)) {
717
+ this._queryManager.unwatchFromQueryKey(key, callbackForRegistration, context);
718
+ } else {
719
+ // fallback to regular watch logic
720
+ (0, _error_utils.invariant)(key.startsWith(WatchableColorInViewKeyPrefix), 'Unexpected key in unwatch: %s', key);
721
+ var viewId = key.substring(WatchableColorInViewKeyPrefix.length);
722
+
723
+ this._parentRecordStore.getViewDataStore(viewId).unwatch('recordColors', this._onRecordColorChange);
724
+
725
+ (0, _get2.default)((0, _getPrototypeOf2.default)(Record.prototype), "unwatch", this).call(this, key, callbackForRegistration, context);
726
+ }
727
+ }
728
+ } catch (err) {
729
+ _didIteratorError5 = true;
730
+ _iteratorError5 = err;
731
+ } finally {
732
+ try {
733
+ if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
734
+ _iterator5.return();
735
+ }
736
+ } finally {
737
+ if (_didIteratorError5) {
738
+ throw _iteratorError5;
739
+ }
740
+ }
741
+ }
742
+
743
+ return validKeys;
484
744
  }
745
+ /** @internal */
746
+
485
747
  }, {
486
748
  key: "_dataOrNullIfDeleted",
487
749
  get: function get() {
488
- var _recordsById$this$_id;
489
-
490
750
  var tableData = this._baseData.tablesById[this.parentTable.id];
491
751
 
492
752
  if (!tableData) {
493
753
  return null;
494
754
  }
495
755
 
496
- var recordsById = tableData.recordsById;
497
- (0, _error_utils.invariant)(recordsById, 'Record data is not loaded');
498
- return (_recordsById$this$_id = recordsById[this._id]) !== null && _recordsById$this$_id !== void 0 ? _recordsById$this$_id : null;
499
- }
500
- /**
501
- * The table that this record belongs to. Should never change because records aren't moved between tables.
502
- *
503
- * @internal (since we may not be able to return parent model instances in the immutable models world)
504
- * @example
505
- * ```js
506
- * import {useRecords} from '@airtable/blocks/ui';
507
- * const records = useRecords(myTable);
508
- * console.log(records[0].parentTable.id === myTable.id);
509
- * // => true
510
- * ```
511
- */
756
+ var isRecordLoaded = this._parentRecordStore.isRecordLoaded(this._id);
757
+
758
+ (0, _error_utils.invariant)(isRecordLoaded, 'Record data is not loaded');
759
+
760
+ var isRecordDeleted = this._parentRecordStore.isRecordDeleted(this._id);
512
761
 
762
+ return isRecordDeleted ? null : true;
763
+ }
513
764
  }, {
514
765
  key: "parentTable",
515
766
  get: function get() {
@@ -551,7 +802,9 @@ function (_AbstractModel) {
551
802
  }, {
552
803
  key: "commentCount",
553
804
  get: function get() {
554
- return this._data.commentCount;
805
+ this._assertExistsAndLoaded();
806
+
807
+ return this._parentRecordStore.getRecordCommentCount(this._id);
555
808
  }
556
809
  /**
557
810
  * The created time of this record.
@@ -567,7 +820,9 @@ function (_AbstractModel) {
567
820
  }, {
568
821
  key: "createdTime",
569
822
  get: function get() {
570
- return new Date(this._data.createdTime);
823
+ this._assertExistsAndLoaded();
824
+
825
+ return this._parentRecordStore.getRecordCreatedTime(this._id);
571
826
  }
572
827
  }]);
573
828
  return Record;
@@ -22,7 +22,7 @@ Object.defineProperty(exports, "__esModule", {
22
22
  value: true
23
23
  });
24
24
  exports.normalizeSortsOrGroups = normalizeSortsOrGroups;
25
- exports.default = void 0;
25
+ exports.default = exports.WatchableRecordQueryResultKeys = void 0;
26
26
 
27
27
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
28
28
 
@@ -61,6 +61,8 @@ var _record_coloring = require("./record_coloring");
61
61
  /** @module @airtable/blocks/models: RecordQueryResult */
62
62
 
63
63
  /** */
64
+
65
+ /** @hidden */
64
66
  var WatchableRecordQueryResultKeys = Object.freeze({
65
67
  records: 'records',
66
68
  recordIds: 'recordIds',
@@ -70,6 +72,7 @@ var WatchableRecordQueryResultKeys = Object.freeze({
70
72
  groupLevels: 'groupLevels',
71
73
  isDataLoaded: 'isDataLoaded'
72
74
  });
75
+ exports.WatchableRecordQueryResultKeys = WatchableRecordQueryResultKeys;
73
76
  var WatchableCellValuesInFieldKeyPrefix = 'cellValuesInField:'; // The string case is to accommodate cellValuesInField:$FieldId.
74
77
 
75
78
  /**