relay-runtime 0.0.0-main-d112a76d → 0.0.0-main-e186ff01
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.
- package/experimental.js +1 -1
- package/index.js +1 -1
- package/lib/store/RelayReader.js +24 -5
- package/lib/util/handlePotentialSnapshotErrors.js +11 -3
- package/package.json +1 -1
- package/store/RelayReader.js.flow +34 -4
- package/store/RelayStoreTypes.js.flow +32 -0
- package/util/handlePotentialSnapshotErrors.js.flow +15 -4
package/experimental.js
CHANGED
package/index.js
CHANGED
package/lib/store/RelayReader.js
CHANGED
|
@@ -186,7 +186,7 @@ var RelayReader = /*#__PURE__*/function () {
|
|
|
186
186
|
!this._variables.hasOwnProperty(name) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayReader(): Undefined variable `%s`.', name) : invariant(false) : void 0;
|
|
187
187
|
return this._variables[name];
|
|
188
188
|
};
|
|
189
|
-
_proto._maybeReportUnexpectedNull = function _maybeReportUnexpectedNull(selection) {
|
|
189
|
+
_proto._maybeReportUnexpectedNull = function _maybeReportUnexpectedNull(selection, fieldValue) {
|
|
190
190
|
if (selection.action === 'NONE') {
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
@@ -202,10 +202,19 @@ var RelayReader = /*#__PURE__*/function () {
|
|
|
202
202
|
var _selection$field$alia;
|
|
203
203
|
fieldName = (_selection$field$alia = selection.field.alias) !== null && _selection$field$alia !== void 0 ? _selection$field$alia : selection.field.name;
|
|
204
204
|
}
|
|
205
|
+
var fieldError = null;
|
|
206
|
+
if (fieldValue === null && this._fieldErrors.length > 0) {
|
|
207
|
+
var lastError = this._fieldErrors[this._fieldErrors.length - 1];
|
|
208
|
+
if (lastError.kind === 'relay_field_payload.error') {
|
|
209
|
+
fieldError = lastError.error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
205
212
|
switch (selection.action) {
|
|
206
213
|
case 'THROW':
|
|
207
214
|
this._fieldErrors.push({
|
|
215
|
+
fieldError: fieldError,
|
|
208
216
|
fieldPath: fieldName,
|
|
217
|
+
fieldValue: fieldValue,
|
|
209
218
|
handled: false,
|
|
210
219
|
kind: 'missing_required_field.throw',
|
|
211
220
|
owner: owner,
|
|
@@ -214,7 +223,9 @@ var RelayReader = /*#__PURE__*/function () {
|
|
|
214
223
|
return;
|
|
215
224
|
case 'LOG':
|
|
216
225
|
this._fieldErrors.push({
|
|
226
|
+
fieldError: fieldError,
|
|
217
227
|
fieldPath: fieldName,
|
|
228
|
+
fieldValue: fieldValue,
|
|
218
229
|
kind: 'missing_required_field.log',
|
|
219
230
|
owner: owner,
|
|
220
231
|
uiContext: undefined
|
|
@@ -226,7 +237,7 @@ var RelayReader = /*#__PURE__*/function () {
|
|
|
226
237
|
};
|
|
227
238
|
_proto._handleRequiredFieldValue = function _handleRequiredFieldValue(selection, value) {
|
|
228
239
|
if (value == null) {
|
|
229
|
-
this._maybeReportUnexpectedNull(selection);
|
|
240
|
+
this._maybeReportUnexpectedNull(selection, value);
|
|
230
241
|
return false;
|
|
231
242
|
}
|
|
232
243
|
return true;
|
|
@@ -281,9 +292,17 @@ var RelayReader = /*#__PURE__*/function () {
|
|
|
281
292
|
message: "Relay: Error in resolver for field at ".concat(error.fieldPath, " in ").concat(error.owner)
|
|
282
293
|
};
|
|
283
294
|
case 'missing_required_field.throw':
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
295
|
+
{
|
|
296
|
+
var reason;
|
|
297
|
+
if (error.fieldValue === null) {
|
|
298
|
+
reason = error.fieldError != null ? "the server returned null with an error: ".concat(error.fieldError.message) : 'the server returned null';
|
|
299
|
+
} else {
|
|
300
|
+
reason = 'the field was missing in the store (data may not have been fetched, or was removed by a graph relationship change: https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change)';
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
message: "Relay: Missing @required value at path '".concat(error.fieldPath, "' in '").concat(error.owner, "': ").concat(reason, ".")
|
|
304
|
+
};
|
|
305
|
+
}
|
|
287
306
|
case 'missing_required_field.log':
|
|
288
307
|
return null;
|
|
289
308
|
default:
|
|
@@ -29,11 +29,19 @@ function handleFieldErrors(environment, fieldErrors, loggingContext) {
|
|
|
29
29
|
case 'relay_resolver.error':
|
|
30
30
|
throw new Error("Relay: Resolver error at path '".concat(_fieldError.fieldPath, "' in '").concat(_fieldError.owner, "'. Message: ").concat(_fieldError.error.message));
|
|
31
31
|
case 'relay_field_payload.error':
|
|
32
|
-
throw new Error("Relay:
|
|
32
|
+
throw new Error("Relay: Received a field error in the server response for field '".concat(_fieldError.fieldPath, "' in '").concat(_fieldError.owner, "'. Message: ").concat(_fieldError.error.message));
|
|
33
33
|
case 'missing_expected_data.throw':
|
|
34
|
-
throw new Error("Relay: Missing expected data at path '".concat(_fieldError.fieldPath, "' in '").concat(_fieldError.owner, "'."));
|
|
34
|
+
throw new Error("Relay: Missing expected data at path '".concat(_fieldError.fieldPath, "' in '").concat(_fieldError.owner, "'. See https://relay.dev/docs/next/debugging/why-null/ for likely causes."));
|
|
35
35
|
case 'missing_required_field.throw':
|
|
36
|
-
|
|
36
|
+
{
|
|
37
|
+
var reason = void 0;
|
|
38
|
+
if (_fieldError.fieldValue === null) {
|
|
39
|
+
reason = _fieldError.fieldError != null ? "the server returned null with an error: ".concat(_fieldError.fieldError.message) : 'the server returned null';
|
|
40
|
+
} else {
|
|
41
|
+
reason = 'the field was missing in the store (data may not have been fetched, or was removed by a graph relationship change: https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change)';
|
|
42
|
+
}
|
|
43
|
+
throw new Error("Relay: Missing @required value at path '".concat(_fieldError.fieldPath, "' in '").concat(_fieldError.owner, "': ").concat(reason, "."));
|
|
44
|
+
}
|
|
37
45
|
case 'missing_required_field.log':
|
|
38
46
|
case 'missing_expected_data.log':
|
|
39
47
|
break;
|
package/package.json
CHANGED
|
@@ -353,7 +353,10 @@ class RelayReader {
|
|
|
353
353
|
return this._variables[name];
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
-
_maybeReportUnexpectedNull(
|
|
356
|
+
_maybeReportUnexpectedNull(
|
|
357
|
+
selection: ReaderRequiredField,
|
|
358
|
+
fieldValue: null | void,
|
|
359
|
+
) {
|
|
357
360
|
if (selection.action === 'NONE') {
|
|
358
361
|
return;
|
|
359
362
|
}
|
|
@@ -371,10 +374,24 @@ class RelayReader {
|
|
|
371
374
|
fieldName = selection.field.alias ?? selection.field.name;
|
|
372
375
|
}
|
|
373
376
|
|
|
377
|
+
// When the field was null (not missing from the store), check whether the
|
|
378
|
+
// server also attached a field error to it. _maybeAddFieldErrors runs in
|
|
379
|
+
// the same call stack just before this method, so any companion error for
|
|
380
|
+
// this field is always the last entry pushed to this._fieldErrors.
|
|
381
|
+
let fieldError = null;
|
|
382
|
+
if (fieldValue === null && this._fieldErrors.length > 0) {
|
|
383
|
+
const lastError = this._fieldErrors[this._fieldErrors.length - 1];
|
|
384
|
+
if (lastError.kind === 'relay_field_payload.error') {
|
|
385
|
+
fieldError = lastError.error;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
374
389
|
switch (selection.action) {
|
|
375
390
|
case 'THROW':
|
|
376
391
|
this._fieldErrors.push({
|
|
392
|
+
fieldError,
|
|
377
393
|
fieldPath: fieldName,
|
|
394
|
+
fieldValue,
|
|
378
395
|
handled: false,
|
|
379
396
|
kind: 'missing_required_field.throw',
|
|
380
397
|
owner,
|
|
@@ -385,7 +402,9 @@ class RelayReader {
|
|
|
385
402
|
return;
|
|
386
403
|
case 'LOG':
|
|
387
404
|
this._fieldErrors.push({
|
|
405
|
+
fieldError,
|
|
388
406
|
fieldPath: fieldName,
|
|
407
|
+
fieldValue,
|
|
389
408
|
kind: 'missing_required_field.log',
|
|
390
409
|
owner,
|
|
391
410
|
// the uiContext is always undefined here.
|
|
@@ -403,7 +422,7 @@ class RelayReader {
|
|
|
403
422
|
value: unknown,
|
|
404
423
|
): boolean /*should continue to siblings*/ {
|
|
405
424
|
if (value == null) {
|
|
406
|
-
this._maybeReportUnexpectedNull(selection);
|
|
425
|
+
this._maybeReportUnexpectedNull(selection, value);
|
|
407
426
|
// We are going to throw, or our parent is going to get nulled out.
|
|
408
427
|
// Either way, sibling values are going to be ignored, so we can
|
|
409
428
|
// bail early here as an optimization.
|
|
@@ -499,12 +518,23 @@ class RelayReader {
|
|
|
499
518
|
return {
|
|
500
519
|
message: `Relay: Error in resolver for field at ${error.fieldPath} in ${error.owner}`,
|
|
501
520
|
};
|
|
502
|
-
case 'missing_required_field.throw':
|
|
521
|
+
case 'missing_required_field.throw': {
|
|
503
522
|
// If we have a nested @required(THROW) that will throw,
|
|
504
523
|
// we want to catch that error and provide it
|
|
524
|
+
let reason: string;
|
|
525
|
+
if (error.fieldValue === null) {
|
|
526
|
+
reason =
|
|
527
|
+
error.fieldError != null
|
|
528
|
+
? `the server returned null with an error: ${error.fieldError.message}`
|
|
529
|
+
: 'the server returned null';
|
|
530
|
+
} else {
|
|
531
|
+
reason =
|
|
532
|
+
'the field was missing in the store (data may not have been fetched, or was removed by a graph relationship change: https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change)';
|
|
533
|
+
}
|
|
505
534
|
return {
|
|
506
|
-
message: `Relay: Missing @required value at path '${error.fieldPath}' in '${error.owner}'.`,
|
|
535
|
+
message: `Relay: Missing @required value at path '${error.fieldPath}' in '${error.owner}': ${reason}.`,
|
|
507
536
|
};
|
|
537
|
+
}
|
|
508
538
|
case 'missing_required_field.log':
|
|
509
539
|
// For backwards compatibility, we don't surface log level missing required fields
|
|
510
540
|
return null;
|
|
@@ -1411,6 +1411,22 @@ export type MissingRequiredFieldLogEvent = {
|
|
|
1411
1411
|
readonly kind: 'missing_required_field.log',
|
|
1412
1412
|
readonly owner: string,
|
|
1413
1413
|
fieldPath: string, // Purposefully mutable to allow lazy construction in RelayReader
|
|
1414
|
+
/**
|
|
1415
|
+
* The value that was read for the @required field:
|
|
1416
|
+
* - `null`: the server returned null for this field. If `fieldError` is also
|
|
1417
|
+
* set, the server attached an error explaining why.
|
|
1418
|
+
* - `undefined` (void): the field was absent from the Relay store entirely.
|
|
1419
|
+
* This is commonly caused by a graph relationship change observed by another
|
|
1420
|
+
* query/mutation, or by an imperative store update that omitted this field.
|
|
1421
|
+
* See https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change
|
|
1422
|
+
*/
|
|
1423
|
+
readonly fieldValue: null | void,
|
|
1424
|
+
/**
|
|
1425
|
+
* When `fieldValue` is `null` and the server attached a field error to this
|
|
1426
|
+
* field in the GraphQL response, this contains that error. This is the most
|
|
1427
|
+
* likely explanation for why the field was null.
|
|
1428
|
+
*/
|
|
1429
|
+
readonly fieldError: ?TRelayFieldError,
|
|
1414
1430
|
// To populate this, you should pass the value to a ReactRelayLoggingContext
|
|
1415
1431
|
readonly uiContext: unknown | void,
|
|
1416
1432
|
};
|
|
@@ -1431,6 +1447,22 @@ export type MissingRequiredFieldThrowEvent = {
|
|
|
1431
1447
|
readonly owner: string,
|
|
1432
1448
|
fieldPath: string, // Purposefully mutable to allow lazy construction in RelayReader
|
|
1433
1449
|
readonly handled: boolean,
|
|
1450
|
+
/**
|
|
1451
|
+
* The value that was read for the @required field:
|
|
1452
|
+
* - `null`: the server returned null for this field. If `fieldError` is also
|
|
1453
|
+
* set, the server attached an error explaining why.
|
|
1454
|
+
* - `undefined` (void): the field was absent from the Relay store entirely.
|
|
1455
|
+
* This is commonly caused by a graph relationship change observed by another
|
|
1456
|
+
* query/mutation, or by an imperative store update that omitted this field.
|
|
1457
|
+
* See https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change
|
|
1458
|
+
*/
|
|
1459
|
+
readonly fieldValue: null | void,
|
|
1460
|
+
/**
|
|
1461
|
+
* When `fieldValue` is `null` and the server attached a field error to this
|
|
1462
|
+
* field in the GraphQL response, this contains that error. This is the most
|
|
1463
|
+
* likely explanation for why the field was null.
|
|
1464
|
+
*/
|
|
1465
|
+
readonly fieldError: ?TRelayFieldError,
|
|
1434
1466
|
// To populate this, you should pass the value to a ReactRelayLoggingContext
|
|
1435
1467
|
readonly uiContext: unknown | void,
|
|
1436
1468
|
};
|
|
@@ -50,16 +50,27 @@ function handleFieldErrors(
|
|
|
50
50
|
);
|
|
51
51
|
case 'relay_field_payload.error':
|
|
52
52
|
throw new Error(
|
|
53
|
-
`Relay:
|
|
53
|
+
`Relay: Received a field error in the server response for field '${fieldError.fieldPath}' in '${fieldError.owner}'. Message: ${fieldError.error.message}`,
|
|
54
54
|
);
|
|
55
55
|
case 'missing_expected_data.throw':
|
|
56
56
|
throw new Error(
|
|
57
|
-
`Relay: Missing expected data at path '${fieldError.fieldPath}' in '${fieldError.owner}'.`,
|
|
57
|
+
`Relay: Missing expected data at path '${fieldError.fieldPath}' in '${fieldError.owner}'. See https://relay.dev/docs/next/debugging/why-null/ for likely causes.`,
|
|
58
58
|
);
|
|
59
|
-
case 'missing_required_field.throw':
|
|
59
|
+
case 'missing_required_field.throw': {
|
|
60
|
+
let reason: string;
|
|
61
|
+
if (fieldError.fieldValue === null) {
|
|
62
|
+
reason =
|
|
63
|
+
fieldError.fieldError != null
|
|
64
|
+
? `the server returned null with an error: ${fieldError.fieldError.message}`
|
|
65
|
+
: 'the server returned null';
|
|
66
|
+
} else {
|
|
67
|
+
reason =
|
|
68
|
+
'the field was missing in the store (data may not have been fetched, or was removed by a graph relationship change: https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change)';
|
|
69
|
+
}
|
|
60
70
|
throw new Error(
|
|
61
|
-
`Relay: Missing @required value at path '${fieldError.fieldPath}' in '${fieldError.owner}'.`,
|
|
71
|
+
`Relay: Missing @required value at path '${fieldError.fieldPath}' in '${fieldError.owner}': ${reason}.`,
|
|
62
72
|
);
|
|
73
|
+
}
|
|
63
74
|
case 'missing_required_field.log':
|
|
64
75
|
case 'missing_expected_data.log':
|
|
65
76
|
// These should have already been filtered out. Sadly, Flow Type
|