@airtable/blocks 1.7.0 → 1.9.0-experimental

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