@airtable/blocks 1.8.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 (93) hide show
  1. package/CHANGELOG.md +16 -2
  2. package/dist/cjs/error_utils.js +17 -0
  3. package/dist/cjs/models/base.js +16 -5
  4. package/dist/cjs/models/cursor.js +2 -0
  5. package/dist/cjs/models/field.js +146 -27
  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 +67 -176
  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 -13
  15. package/dist/cjs/models/table_or_view_query_result.js +480 -414
  16. package/dist/cjs/models/view_data_store.js +243 -295
  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/mutations.js +1 -0
  22. package/dist/cjs/ui/icon_config.js +4 -2
  23. package/dist/cjs/ui/use_global_config.js +1 -1
  24. package/dist/cjs/ui/use_records.js +5 -1
  25. package/dist/cjs/unstable_testing_utils.js +2 -2
  26. package/dist/cjs/watchable.js +123 -71
  27. package/dist/types/src/models/base.d.ts +10 -3
  28. package/dist/types/src/models/base.d.ts.map +1 -1
  29. package/dist/types/src/models/cursor.d.ts +2 -0
  30. package/dist/types/src/models/cursor.d.ts.map +1 -1
  31. package/dist/types/src/models/field.d.ts +65 -1
  32. package/dist/types/src/models/field.d.ts.map +1 -1
  33. package/dist/types/src/models/grouped_record_query_result.d.ts +3 -3
  34. package/dist/types/src/models/grouped_record_query_result.d.ts.map +1 -1
  35. package/dist/types/src/models/linked_records_query_result.d.ts.map +1 -1
  36. package/dist/types/src/models/mutation_constants.d.ts +1 -0
  37. package/dist/types/src/models/mutation_constants.d.ts.map +1 -1
  38. package/dist/types/src/models/mutations.d.ts.map +1 -1
  39. package/dist/types/src/models/query_manager.d.ts +2 -0
  40. package/dist/types/src/models/query_manager.d.ts.map +1 -0
  41. package/dist/types/src/models/record.d.ts +12 -3
  42. package/dist/types/src/models/record.d.ts.map +1 -1
  43. package/dist/types/src/models/record_query_result.d.ts +3 -2
  44. package/dist/types/src/models/record_query_result.d.ts.map +1 -1
  45. package/dist/types/src/models/record_store.d.ts.map +1 -1
  46. package/dist/types/src/models/table.d.ts +8 -4
  47. package/dist/types/src/models/table.d.ts.map +1 -1
  48. package/dist/types/src/models/table_or_view_query_result.d.ts +3 -5
  49. package/dist/types/src/models/table_or_view_query_result.d.ts.map +1 -1
  50. package/dist/types/src/models/view_data_store.d.ts +0 -1
  51. package/dist/types/src/models/view_data_store.d.ts.map +1 -1
  52. package/dist/types/src/models/view_metadata_query_result.d.ts +1 -1
  53. package/dist/types/src/models/view_metadata_query_result.d.ts.map +1 -1
  54. package/dist/types/src/private_utils.d.ts +30 -1
  55. package/dist/types/src/private_utils.d.ts.map +1 -1
  56. package/dist/types/src/sdk.d.ts.map +1 -1
  57. package/dist/types/src/testing/{mock_airtable_interface.d.ts → abstract_mock_airtable_interface.d.ts} +20 -15
  58. package/dist/types/src/testing/abstract_mock_airtable_interface.d.ts.map +1 -0
  59. package/dist/types/src/types/airtable_interface.d.ts +43 -19
  60. package/dist/types/src/types/airtable_interface.d.ts.map +1 -1
  61. package/dist/types/src/types/block_query_spec.d.ts +139 -0
  62. package/dist/types/src/types/block_query_spec.d.ts.map +1 -0
  63. package/dist/types/src/types/field.d.ts +80 -44
  64. package/dist/types/src/types/field.d.ts.map +1 -1
  65. package/dist/types/src/types/mutations.d.ts +31 -3
  66. package/dist/types/src/types/mutations.d.ts.map +1 -1
  67. package/dist/types/src/types/table.d.ts +0 -2
  68. package/dist/types/src/types/table.d.ts.map +1 -1
  69. package/dist/types/src/types/view.d.ts +3 -8
  70. package/dist/types/src/types/view.d.ts.map +1 -1
  71. package/dist/types/src/ui/icon_config.d.ts +5 -3
  72. package/dist/types/src/ui/icon_config.d.ts.map +1 -1
  73. package/dist/types/src/ui/link.d.ts +1 -1
  74. package/dist/types/src/ui/link.d.ts.map +1 -1
  75. package/dist/types/src/ui/use_global_config.d.ts +1 -1
  76. package/dist/types/src/unstable_testing_utils.d.ts +1 -1
  77. package/dist/types/src/unstable_testing_utils.d.ts.map +1 -1
  78. package/dist/types/src/watchable.d.ts.map +1 -1
  79. package/dist/types/test/airtable_interface_mocks/fixture_data.d.ts +121 -0
  80. package/dist/types/test/airtable_interface_mocks/fixture_data.d.ts.map +1 -0
  81. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts +2 -2
  82. package/dist/types/test/airtable_interface_mocks/linked_records.d.ts.map +1 -1
  83. package/dist/types/test/airtable_interface_mocks/{mock_airtable_interface_internal.d.ts → mock_airtable_interface.d.ts} +26 -18
  84. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface.d.ts.map +1 -0
  85. package/dist/types/test/airtable_interface_mocks/mock_base_data_stores.d.ts +51 -0
  86. package/dist/types/test/airtable_interface_mocks/mock_base_data_stores.d.ts.map +1 -0
  87. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts +2 -2
  88. package/dist/types/test/airtable_interface_mocks/project_tracker.d.ts.map +1 -1
  89. package/dist/types/test/test_helpers.d.ts +2 -0
  90. package/dist/types/test/test_helpers.d.ts.map +1 -1
  91. package/package.json +3 -1
  92. package/dist/types/src/testing/mock_airtable_interface.d.ts.map +0 -1
  93. package/dist/types/test/airtable_interface_mocks/mock_airtable_interface_internal.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -9,9 +9,23 @@ Not every commit needs to result in a change to this file (e.g. docs and chore c
9
9
  commit that affects the code in a way that consumers might care about should include edits to the
10
10
  'Unreleased' section though. Breaking changes should be prefixed with `**BREAKING:**`.
11
11
 
12
- ## [Unreleased](https://github.com/airtable/blocks/compare/@airtable/blocks@1.8.0...HEAD)
12
+ ## [Unreleased](https://github.com/airtable/blocks/compare/@airtable/blocks@1.9.0...HEAD)
13
13
 
14
- No changes.
14
+ <<<<<<< seankeenan-at-fixTypoAndFormattingFromOpenPR
15
+
16
+ - # Fix typo in cursor.ts documentation - thanks @m2creates!
17
+ - Add new `calendarDay` icon (and micro variant)
18
+ > > > > > > > main
19
+
20
+ ## [1.9.0](https://github.com/airtable/blocks/compare/@airtable/blocks@1.8.0...@airtable/blocks@1.9.0) - 2021-09-15
21
+
22
+ - Update documentation for `FieldType.CHECKBOX` cell read & write types to be more accurate.
23
+ - Fetching field types & configs is now cached, making it more performant.
24
+ - Field descriptions can now be edited and specified when creating a new field:
25
+ - Added `field.updateDescriptionAsync`.
26
+ - Added optional `description` argument to `table.createFieldAsync`.
27
+ - Added optional `description` property to `field` objects accepted by
28
+ `base.createTableAsync`.
15
29
 
16
30
  ## [1.8.0](https://github.com/airtable/blocks/compare/@airtable/blocks@1.7.2...@airtable/blocks@1.8.0) - 2021-07-12
17
31
 
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.spawnError = spawnError;
9
9
  exports.invariant = invariant;
10
10
  exports.spawnUnknownSwitchCaseError = spawnUnknownSwitchCaseError;
11
+ exports.spawnExhaustiveSwitchError = spawnExhaustiveSwitchError;
11
12
  exports.spawnAbstractMethodError = spawnAbstractMethodError;
12
13
 
13
14
  // If errorOriginFn is specified, all frames above and including the call to errorOriginFn
@@ -79,6 +80,22 @@ function spawnUnknownSwitchCaseError(valueDescription, providedValue, key) {
79
80
  var providedValueKeyString = providedValueKey !== null && providedValueKey !== undefined ? providedValueKey : 'null';
80
81
  return spawnErrorWithOriginOmittedFromStackTrace('Unknown value %s for %s', [providedValueKeyString, valueDescription], spawnUnknownSwitchCaseError);
81
82
  }
83
+ /**
84
+ * Forces TypeScript to prove that calling this function is impossible at a
85
+ * type level by accepting `never`. In the unlikely case this function is
86
+ * called we infer a useful error message.
87
+ *
88
+ * @internal
89
+ */
90
+
91
+
92
+ function spawnExhaustiveSwitchError(impossibleValue) {
93
+ if (impossibleValue === null) {
94
+ return spawnErrorWithOriginOmittedFromStackTrace('Unexpected null in exhaustive switch', undefined, spawnExhaustiveSwitchError);
95
+ } else {
96
+ return spawnErrorWithOriginOmittedFromStackTrace("Unexpected %s in exhaustive switch", [typeof impossibleValue], spawnExhaustiveSwitchError);
97
+ }
98
+ }
82
99
  /**
83
100
  * @internal
84
101
  */
@@ -471,7 +471,8 @@ function (_AbstractModel) {
471
471
  type: field.type
472
472
  }, field.options ? {
473
473
  options: field.options
474
- } : null) : undefined
474
+ } : null) : undefined,
475
+ description: field.description
475
476
  };
476
477
  })
477
478
  });
@@ -505,7 +506,8 @@ function (_AbstractModel) {
505
506
  * Creates a new table.
506
507
  *
507
508
  * Throws an error if the user does not have permission to create a table, if an invalid
508
- * table name is provided, or if invalid fields are provided (invalid name, type or options).
509
+ * table name is provided, or if invalid fields are provided (invalid name, type, options or
510
+ * description).
509
511
  *
510
512
  * Refer to {@link FieldType} for supported field types, the write format for field options, and
511
513
  * other specifics for certain field types.
@@ -522,7 +524,10 @@ function (_AbstractModel) {
522
524
  * table in your app.
523
525
  *
524
526
  * @param name name for the table. must be case-insensitive unique
525
- * @param fields array of fields to create in the table: see below for details.
527
+ * @param fields array of fields to create in the table: see below for an example. `name` and
528
+ * `type` must be specified for all fields, while `options` is only required for fields that
529
+ * have field options. `description` is optional and will be `''` if not specified or if
530
+ * specified as `null`.
526
531
  *
527
532
  * @example
528
533
  * ```js
@@ -530,7 +535,7 @@ function (_AbstractModel) {
530
535
  * const name = 'My new table';
531
536
  * const fields = [
532
537
  * // Name will be the primary field of the table.
533
- * {name: 'Name', type: FieldType.SINGLE_LINE_TEXT},
538
+ * {name: 'Name', type: FieldType.SINGLE_LINE_TEXT, description: 'This is the primary field'},
534
539
  * {name: 'Notes', type: FieldType.RICH_TEXT},
535
540
  * {name: 'Attachments', type: FieldType.MULTIPLE_ATTACHMENTS},
536
541
  * {name: 'Number', type: FieldType.NUMBER, options: {
@@ -566,13 +571,19 @@ function (_AbstractModel) {
566
571
  type: _mutations.MutationTypes.CREATE_SINGLE_TABLE,
567
572
  name,
568
573
  fields: fields.map(field => {
574
+ var _field$description;
575
+
569
576
  return {
570
577
  name: field.name,
571
578
  config: _objectSpread({
572
579
  type: field.type
573
580
  }, field.options ? {
574
581
  options: field.options
575
- } : null)
582
+ } : null),
583
+ // Coerce undefined to null so that only old SDKs pass "undefined" for description
584
+ // '' is permitted, as we already set empty descriptions to '' when editing descriptions
585
+ // from the UI
586
+ description: (_field$description = field.description) !== null && _field$description !== void 0 ? _field$description : null
576
587
  };
577
588
  })
578
589
  }));
@@ -89,6 +89,7 @@ var WatchableCursorKeys = Object.freeze({
89
89
  * return (
90
90
  * <div>
91
91
  * Active table: {cursor.activeTableId}
92
+ * <br />
92
93
  * Active view: {cursor.activeViewId}
93
94
  * </div>
94
95
  * );
@@ -109,6 +110,7 @@ var WatchableCursorKeys = Object.freeze({
109
110
  * return (
110
111
  * <div>
111
112
  * Selected records: {cursor.selectedRecordIds.join(', ')}
113
+ * <br />
112
114
  * Selected fields: {cursor.selectedFieldIds.join(', ')}
113
115
  * </div>
114
116
  * );
@@ -109,7 +109,9 @@ function (_AbstractModel) {
109
109
  (0, _classCallCheck2.default)(this, Field);
110
110
  _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(Field).call(this, sdk, fieldId));
111
111
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_parentTable", void 0);
112
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_cachedFieldTypeConfigOrNull", void 0);
112
113
  _this._parentTable = parentTable;
114
+ _this._cachedFieldTypeConfigOrNull = null;
113
115
  return _this;
114
116
  }
115
117
  /**
@@ -118,6 +120,42 @@ function (_AbstractModel) {
118
120
 
119
121
 
120
122
  (0, _createClass2.default)(Field, [{
123
+ key: "_getCachedConfigFromFieldTypeProvider",
124
+ // We use a cached response from FieldTypeProvider because getting the config can
125
+ // be an expensive operation. In particular when fieldConfigs are extremely large
126
+ // (eg: Select fields with lots of select options)
127
+ value: function _getCachedConfigFromFieldTypeProvider() {
128
+ if (this._cachedFieldTypeConfigOrNull !== null) {
129
+ return this._cachedFieldTypeConfigOrNull;
130
+ }
131
+
132
+ var airtableInterface = this._sdk.__airtableInterface;
133
+ var appInterface = this._sdk.__appInterface;
134
+ this._cachedFieldTypeConfigOrNull = airtableInterface.fieldTypeProvider.getConfig(appInterface, this._data, this.parentTable.__getFieldNamesById());
135
+ return this._cachedFieldTypeConfigOrNull;
136
+ }
137
+ }, {
138
+ key: "_clearCachedConfig",
139
+ value: function _clearCachedConfig() {
140
+ this._cachedFieldTypeConfigOrNull = null;
141
+ }
142
+ /**
143
+ * The type and options of the field to make type narrowing `FieldOptions` easier.
144
+ *
145
+ * @see {@link FieldConfig}
146
+ * @example
147
+ * const fieldConfig = field.config;
148
+ * if (fieldConfig.type === FieldType.SINGLE_SELECT) {
149
+ * return fieldConfig.options.choices;
150
+ * } else if (fieldConfig.type === FieldType.MULTIPLE_LOOKUP_VALUES && fieldConfig.options.isValid) {
151
+ * if (fieldConfig.options.result.type === FieldType.SINGLE_SELECT) {
152
+ * return fieldConfig.options.result.options.choices;
153
+ * }
154
+ * }
155
+ * return DEFAULT_CHOICES;
156
+ */
157
+
158
+ }, {
121
159
  key: "checkPermissionsForUpdateOptions",
122
160
 
123
161
  /**
@@ -125,7 +163,7 @@ function (_AbstractModel) {
125
163
  *
126
164
  * Accepts partial input, in the same format as {@link updateOptionsAsync}.
127
165
  *
128
- * Returns `{hasPermission: true}` if the current user can update the specified record,
166
+ * Returns `{hasPermission: true}` if the current user can update the specified field,
129
167
  * `{hasPermission: false, reasonDisplayString: string}` otherwise. `reasonDisplayString` may be
130
168
  * used to display an error message to the user.
131
169
  *
@@ -241,6 +279,104 @@ function (_AbstractModel) {
241
279
  }
242
280
  }, null, this);
243
281
  }
282
+ /**
283
+ * Checks whether the current user has permission to perform the given description update.
284
+ *
285
+ * Accepts partial input, in the same format as {@link updateDescriptionAsync}.
286
+ *
287
+ * Returns `{hasPermission: true}` if the current user can update the specified field,
288
+ * `{hasPermission: false, reasonDisplayString: string}` otherwise. `reasonDisplayString` may be
289
+ * used to display an error message to the user.
290
+ *
291
+ * @param description new description for the field
292
+ *
293
+ * @example
294
+ * ```js
295
+ * const updateFieldCheckResult = field.checkPermissionsForUpdateDescription();
296
+ *
297
+ * if (!updateFieldCheckResult.hasPermission) {
298
+ * alert(updateFieldCheckResult.reasonDisplayString);
299
+ * }
300
+ * ```
301
+ */
302
+
303
+ }, {
304
+ key: "checkPermissionsForUpdateDescription",
305
+ value: function checkPermissionsForUpdateDescription(description) {
306
+ return this._sdk.__mutations.checkPermissionsForMutation({
307
+ type: _mutations.MutationTypes.UPDATE_SINGLE_FIELD_DESCRIPTION,
308
+ tableId: this.parentTable.id,
309
+ id: this.id,
310
+ description
311
+ });
312
+ }
313
+ /**
314
+ * An alias for `checkPermissionsForUpdateDescription(options).hasPermission`.
315
+ *
316
+ * Checks whether the current user has permission to perform the description update.
317
+ *
318
+ * Accepts partial input, in the same format as {@link updateDescriptionAsync}.
319
+ *
320
+ * @param description new description for the field
321
+ *
322
+ * @example
323
+ * ```js
324
+ * const canUpdateField = field.hasPermissionToUpdateDescription();
325
+ *
326
+ * if (!canUpdateField) {
327
+ * alert('not allowed!');
328
+ * }
329
+ * ```
330
+ */
331
+
332
+ }, {
333
+ key: "hasPermissionToUpdateDescription",
334
+ value: function hasPermissionToUpdateDescription(description) {
335
+ return this.checkPermissionsForUpdateDescription(description).hasPermission;
336
+ }
337
+ /**
338
+ * Updates the description for this field.
339
+ *
340
+ * To remove an existing description, pass `''` as the new description.
341
+ * `null` is also accepted and will be coerced to `''` for consistency with field creation.
342
+ *
343
+ * Throws an error if the user does not have permission to update the field, or if an invalid
344
+ * description is provided.
345
+ *
346
+ * This action is asynchronous. Unlike updates to cell values, updates to field descriptions are
347
+ * **not** applied optimistically locally. You must `await` the returned promise before
348
+ * relying on the change in your app.
349
+ *
350
+ * @param description new description for the field
351
+ *
352
+ * @example
353
+ * ```js
354
+ * await myTextField.updateDescriptionAsync('This is a text field');
355
+ * ```
356
+ */
357
+
358
+ }, {
359
+ key: "updateDescriptionAsync",
360
+ value: function updateDescriptionAsync(description) {
361
+ return _regenerator.default.async(function updateDescriptionAsync$(_context2) {
362
+ while (1) {
363
+ switch (_context2.prev = _context2.next) {
364
+ case 0:
365
+ _context2.next = 2;
366
+ return _regenerator.default.awrap(this._sdk.__mutations.applyMutationAsync({
367
+ type: _mutations.MutationTypes.UPDATE_SINGLE_FIELD_DESCRIPTION,
368
+ tableId: this.parentTable.id,
369
+ id: this.id,
370
+ description
371
+ }));
372
+
373
+ case 2:
374
+ case "end":
375
+ return _context2.stop();
376
+ }
377
+ }
378
+ }, null, this);
379
+ }
244
380
  /**
245
381
  * `true` if this field is computed, `false` otherwise. A field is
246
382
  * "computed" if it's value is not set by user input (e.g. autoNumber, formula,
@@ -329,6 +465,9 @@ function (_AbstractModel) {
329
465
  }, {
330
466
  key: "__triggerOnChangeForDirtyPaths",
331
467
  value: function __triggerOnChangeForDirtyPaths(dirtyPaths) {
468
+ // Always clear the cached config when anything on the field data model changes
469
+ this._clearCachedConfig();
470
+
332
471
  if (dirtyPaths.name) {
333
472
  this._onChange(WatchableFieldKeys.name);
334
473
  }
@@ -402,11 +541,8 @@ function (_AbstractModel) {
402
541
  }, {
403
542
  key: "type",
404
543
  get: function get() {
405
- var airtableInterface = this._sdk.__airtableInterface;
406
- var appInterface = this._sdk.__appInterface;
407
-
408
- var _airtableInterface$fi = airtableInterface.fieldTypeProvider.getConfig(appInterface, this._data, this.parentTable.__getFieldNamesById()),
409
- type = _airtableInterface$fi.type; // We intend to switch from "lookup" to "multipleLookupValues", but need to support both
544
+ var _this$_getCachedConfi = this._getCachedConfigFromFieldTypeProvider(),
545
+ type = _this$_getCachedConfi.type; // We intend to switch from "lookup" to "multipleLookupValues", but need to support both
410
546
  // until the transition is complete. See <https://airtable.quip.com/VxaMAmAfUscs> for more.
411
547
  // @ts-ignore
412
548
 
@@ -437,31 +573,14 @@ function (_AbstractModel) {
437
573
  }, {
438
574
  key: "options",
439
575
  get: function get() {
440
- var airtableInterface = this._sdk.__airtableInterface;
441
- var appInterface = this._sdk.__appInterface;
442
-
443
- var _airtableInterface$fi2 = airtableInterface.fieldTypeProvider.getConfig(appInterface, this._data, this.parentTable.__getFieldNamesById()),
444
- options = _airtableInterface$fi2.options; // TODO(emma): can we remove this cloneDeep?
576
+ var _this$_getCachedConfi2 = this._getCachedConfigFromFieldTypeProvider(),
577
+ options = _this$_getCachedConfi2.options; // TODO: In the next breaking release freeze (inside of the cache) and replace
578
+ // FieldOptions with readonly<FieldOptions>.
579
+ // Today this is required because we re-use the fieldTypeProvider.getConfig response.
445
580
 
446
581
 
447
582
  return options ? (0, _private_utils.cloneDeep)(options) : null;
448
583
  }
449
- /**
450
- * The type and options of the field to make type narrowing `FieldOptions` easier.
451
- *
452
- * @see {@link FieldConfig}
453
- * @example
454
- * const fieldConfig = field.config;
455
- * if (fieldConfig.type === FieldType.SINGLE_SELECT) {
456
- * return fieldConfig.options.choices;
457
- * } else if (fieldConfig.type === FieldType.MULTIPLE_LOOKUP_VALUES && fieldConfig.options.isValid) {
458
- * if (fieldConfig.options.result.type === FieldType.SINGLE_SELECT) {
459
- * return fieldConfig.options.result.options.choices;
460
- * }
461
- * }
462
- * return DEFAULT_CHOICES;
463
- */
464
-
465
584
  }, {
466
585
  key: "config",
467
586
  get: function get() {
@@ -245,19 +245,13 @@ function (_RecordQueryResult) {
245
245
  while (1) {
246
246
  switch (_context2.prev = _context2.next) {
247
247
  case 0:
248
- this._parentQueryResult.__groupedRecordQueryResultPool.registerObjectForReuseStrong(this); // Ensure we invalidate our memoized computed recordIds whenever a relevant hook changes
249
- // TODO: (SeanKeenan) At the moment this should never be relevant, because groups are recreated
250
- // anytime the groups change - but this is how it should work once groups persist AND
251
- // watching recordIds only changes if records in this group change.
252
- // In the meantime this does ensure that a deleted GroupedRecordQuery with stale recordIds
253
- // doesn't return the cached array.
248
+ // TODO: This entire class assumes it is torn down and re-created by the parent whenever
249
+ // a change to the group is made.
250
+ this._parentQueryResult.__groupedRecordQueryResultPool.registerObjectForReuseStrong(this);
254
251
 
255
-
256
- this.watch(['recordIds', 'groups', 'groupLevels'], this._invalidateComputedRecordIds, this);
257
- this.watch(['recordIds'], this._invalidateRecordIdsSet, this);
258
252
  return _context2.abrupt("return", this._getChangedKeysOnLoad());
259
253
 
260
- case 4:
254
+ case 2:
261
255
  case "end":
262
256
  return _context2.stop();
263
257
  }
@@ -269,10 +263,7 @@ function (_RecordQueryResult) {
269
263
  }, {
270
264
  key: "_unloadData",
271
265
  value: function _unloadData() {
272
- // Ensure we invalidate our memoized computed recordIds whenever a relevant hook changes
273
- this.unwatch(['recordIds', 'groups', 'groupLevels'], this._invalidateComputedRecordIds, this);
274
- this.unwatch(['recordIds'], this._invalidateRecordIdsSet, this); // Invalidate both of the caches, as this object can no longer be accessed
275
-
266
+ // Invalidate both of the caches, as this object can no longer be accessed
276
267
  this._invalidateComputedRecordIds();
277
268
 
278
269
  this._invalidateRecordIdsSet();
@@ -29,6 +29,8 @@ Object.defineProperty(exports, "__esModule", {
29
29
  });
30
30
  exports.default = exports.getLinkedTableId = void 0;
31
31
 
32
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
33
+
32
34
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
33
35
 
34
36
  require("regenerator-runtime/runtime");
@@ -53,8 +55,12 @@ var _field = require("../types/field");
53
55
 
54
56
  var _error_utils = require("../error_utils");
55
57
 
58
+ var _block_query_spec = require("../types/block_query_spec");
59
+
56
60
  var _record_query_result = _interopRequireDefault(require("./record_query_result"));
57
61
 
62
+ var _record_store = require("./record_store");
63
+
58
64
  /** @module @airtable/blocks/models: RecordQueryResult */
59
65
 
60
66
  /** */
@@ -118,6 +124,8 @@ function (_RecordQueryResult) {
118
124
 
119
125
  /** @internal */
120
126
 
127
+ /** @internal */
128
+
121
129
  /** @internal */
122
130
  function LinkedRecordsQueryResult(record, field, normalizedOpts, sdk) {
123
131
  var _this;
@@ -134,6 +142,7 @@ function (_RecordQueryResult) {
134
142
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_computedRecordIdsSet", null);
135
143
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_computedFilteredSortedRecordIds", null);
136
144
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_cellValueChangeHandlerByFieldId", {});
145
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "_recordStoreQueryId", null);
137
146
  (0, _error_utils.invariant)(record.parentTable === field.parentTable, 'record and field must belong to the same table');
138
147
  _this._record = record;
139
148
  _this._field = field;
@@ -279,7 +288,7 @@ function (_RecordQueryResult) {
279
288
  }, {
280
289
  key: "_loadDataAsync",
281
290
  value: function _loadDataAsync() {
282
- var initiallyLoaded, changedKeys, fieldIds, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, fieldId;
291
+ var initiallyLoaded, _ref, _ref2, recordStoreQueryId, changedKeys, fieldIds, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, fieldId;
283
292
 
284
293
  return _regenerator.default.async(function _loadDataAsync$(_context2) {
285
294
  while (1) {
@@ -291,11 +300,21 @@ function (_RecordQueryResult) {
291
300
 
292
301
  this._watchLinkedQueryResult();
293
302
 
294
- initiallyLoaded = this._linkedQueryResult.isDataLoaded;
303
+ initiallyLoaded = this._linkedQueryResult.isDataLoaded; // https://airtable.com/appahuJQw7qfQTBBU/tblSbpimM8Spp9txp/viweAFIus3MhU9Gxa/recNjFYJpeV23LPhH?blocks=hide
304
+ // TODO: (#proj-blocks-sdk-record-limits) Currently queries all cell values in a table
305
+ // when loading linked record data. Must add a LinkedRecord query type in the future.
306
+ // Since _loadDataAsync can only be called once, this recordStoreQueryId is only set once,
307
+ // although it is guaranteed to be stable if needed (so long as this._field.id is stable)
308
+
295
309
  _context2.next = 6;
296
- return _regenerator.default.awrap(Promise.all([this._sdk.base.__getRecordStore(this._record.parentTable.id).loadCellValuesInFieldIdsAsync([this._field.id]), this._linkedQueryResult.loadDataAsync(), this._loadRecordColorsAsync()]));
310
+ return _regenerator.default.awrap(Promise.all([this._sdk.base.__getRecordStore(this._record.parentTable.id).loadAllCellValuesInFieldIdsAsync([this._field.id]), this._linkedQueryResult.loadDataAsync(), this._loadRecordColorsAsync()]));
297
311
 
298
312
  case 6:
313
+ _ref = _context2.sent;
314
+ _ref2 = (0, _slicedToArray2.default)(_ref, 1);
315
+ recordStoreQueryId = _ref2[0];
316
+ this._recordStoreQueryId = recordStoreQueryId;
317
+
299
318
  this._invalidateComputedData();
300
319
 
301
320
  changedKeys = ['records', 'recordIds', 'recordColors']; // If the linked query result was not initially loaded, then the
@@ -311,55 +330,55 @@ function (_RecordQueryResult) {
311
330
  _iteratorNormalCompletion3 = true;
312
331
  _didIteratorError3 = false;
313
332
  _iteratorError3 = undefined;
314
- _context2.prev = 13;
333
+ _context2.prev = 17;
315
334
 
316
335
  for (_iterator3 = fieldIds[Symbol.iterator](); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
317
336
  fieldId = _step3.value;
318
337
  changedKeys.push(_record_query_result.default.WatchableCellValuesInFieldKeyPrefix + fieldId);
319
338
  }
320
339
 
321
- _context2.next = 21;
340
+ _context2.next = 25;
322
341
  break;
323
342
 
324
- case 17:
325
- _context2.prev = 17;
326
- _context2.t0 = _context2["catch"](13);
343
+ case 21:
344
+ _context2.prev = 21;
345
+ _context2.t0 = _context2["catch"](17);
327
346
  _didIteratorError3 = true;
328
347
  _iteratorError3 = _context2.t0;
329
348
 
330
- case 21:
331
- _context2.prev = 21;
332
- _context2.prev = 22;
349
+ case 25:
350
+ _context2.prev = 25;
351
+ _context2.prev = 26;
333
352
 
334
353
  if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
335
354
  _iterator3.return();
336
355
  }
337
356
 
338
- case 24:
339
- _context2.prev = 24;
357
+ case 28:
358
+ _context2.prev = 28;
340
359
 
341
360
  if (!_didIteratorError3) {
342
- _context2.next = 27;
361
+ _context2.next = 31;
343
362
  break;
344
363
  }
345
364
 
346
365
  throw _iteratorError3;
347
366
 
348
- case 27:
349
- return _context2.finish(24);
367
+ case 31:
368
+ return _context2.finish(28);
350
369
 
351
- case 28:
352
- return _context2.finish(21);
370
+ case 32:
371
+ return _context2.finish(25);
353
372
 
354
- case 29:
373
+ case 33:
355
374
  return _context2.abrupt("return", changedKeys);
356
375
 
357
- case 30:
376
+ case 34:
358
377
  case "end":
359
378
  return _context2.stop();
360
379
  }
361
380
  }
362
- }, null, this, [[13, 17, 21, 29], [22,, 24, 28]]);
381
+ }, null, this, [[17, 21, 25, 33], [26,, 28, 32]]);
363
382
  }
364
383
  /** @internal */
365
384
 
@@ -376,7 +395,9 @@ function (_RecordQueryResult) {
376
395
 
377
396
  this._unwatchLinkedQueryResult();
378
397
 
379
- this._sdk.base.__getRecordStore(this._record.parentTable.id).unloadCellValuesInFieldIds([this._field.id]);
398
+ (0, _error_utils.invariant)(this._recordStoreQueryId, 'Unsubscribing from linked record record store without queryId');
399
+
400
+ this._sdk.base.__getRecordStore(this._record.parentTable.id).unloadCellValuesForQueryIdsThatAreSubscribedToAllCells([this._recordStoreQueryId]);
380
401
 
381
402
  this._linkedQueryResult.unloadData();
382
403
 
@@ -448,7 +469,12 @@ function (_RecordQueryResult) {
448
469
  // result
449
470
 
450
471
 
451
- this._originRecordStore.watch('recordIds', this._onOriginRecordsChange, this); // if the linked field in the origin table is deleted, we need to
472
+ this._originRecordStore.watchWithRecordQueryResult({
473
+ recordQueryResult: this,
474
+ keys: _record_store.WatchableRecordStoreKeys.recordIds,
475
+ callbackForRegistration: this._onOriginRecordsChange,
476
+ context: this
477
+ }); // if the linked field in the origin table is deleted, we need to
452
478
  // invalidate this result
453
479
 
454
480
 
@@ -592,11 +618,12 @@ function (_RecordQueryResult) {
592
618
  if (Array.isArray(recordIds)) {
593
619
  var recordIdsSet = this._getOrGenerateRecordIdsSet();
594
620
 
595
- var filteredRecordIds = recordIds.filter(id => typeof id === 'string' && recordIdsSet[id] === true);
621
+ var filteredRecordIds = recordIds.filter(id => typeof id === 'string' && recordIdsSet[id] === true); // There should always be a filteredRecordIds found for this query
622
+ // type - not finding any is a fundamental mistake in our query engine
596
623
 
597
- if (filteredRecordIds.length) {
598
- this._onChange(_record_query_result.default.WatchableCellValuesInFieldKeyPrefix + fieldId, filteredRecordIds);
599
- }
624
+ (0, _error_utils.invariant)(filteredRecordIds.length, 'Filtered record change on incorrect record, query engine issue');
625
+
626
+ this._onChange(_record_query_result.default.WatchableCellValuesInFieldKeyPrefix + fieldId, filteredRecordIds);
600
627
  } else {
601
628
  this._onChange(_record_query_result.default.WatchableCellValuesInFieldKeyPrefix + fieldId);
602
629
  }
@@ -776,6 +803,27 @@ function (_RecordQueryResult) {
776
803
  (0, _error_utils.invariant)(recordIdsSet, 'recordIdsSet must exist');
777
804
  return recordIdsSet;
778
805
  }
806
+ /**
807
+ * This allows a record query result to specify the source of the query
808
+ * eg: View, Table, Linked record when watching fields.
809
+ * This scopes the watch to those fields + rows in the query.
810
+ *
811
+ * @internal
812
+ */
813
+
814
+ }, {
815
+ key: "__constructQuerySpecForBlockFieldSelectionSpec",
816
+ value: function __constructQuerySpecForBlockFieldSelectionSpec(fieldSelection) {
817
+ // The origin tableId is always the one referred to here as we must watch the origin record for deletion
818
+ var originTableId = this._record.parentTable.id;
819
+ return {
820
+ sourceType: _block_query_spec.BlockQuerySourceType.TABLE,
821
+ sourceTableId: originTableId,
822
+ recordSelection: {
823
+ fieldSelection
824
+ }
825
+ };
826
+ }
779
827
  }, {
780
828
  key: "isValid",
781
829
  get: function get() {
@@ -3,10 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.MAX_NUM_FIELDS_PER_TABLE = exports.MAX_TABLE_NAME_LENGTH = exports.MAX_FIELD_NAME_LENGTH = void 0;
6
+ exports.MAX_NUM_FIELDS_PER_TABLE = exports.MAX_TABLE_NAME_LENGTH = exports.MAX_FIELD_DESCRIPTION_LENGTH = exports.MAX_FIELD_NAME_LENGTH = void 0;
7
7
  // Mirrored from clientServerSharedConfigSettings
8
8
  var MAX_FIELD_NAME_LENGTH = 255;
9
9
  exports.MAX_FIELD_NAME_LENGTH = MAX_FIELD_NAME_LENGTH;
10
+ var MAX_FIELD_DESCRIPTION_LENGTH = 20000;
11
+ exports.MAX_FIELD_DESCRIPTION_LENGTH = MAX_FIELD_DESCRIPTION_LENGTH;
10
12
  var MAX_TABLE_NAME_LENGTH = 255;
11
13
  exports.MAX_TABLE_NAME_LENGTH = MAX_TABLE_NAME_LENGTH;
12
14
  var MAX_NUM_FIELDS_PER_TABLE = 500;