@aws-amplify/data-schema 1.3.10 → 1.4.0

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 (102) hide show
  1. package/dist/cjs/ModelType.js +5 -0
  2. package/dist/cjs/ModelType.js.map +1 -1
  3. package/dist/cjs/SchemaProcessor.js +58 -1
  4. package/dist/cjs/SchemaProcessor.js.map +1 -1
  5. package/dist/cjs/runtime/addSchemaToClient.js +1 -0
  6. package/dist/cjs/runtime/addSchemaToClient.js.map +1 -1
  7. package/dist/cjs/runtime/addSchemaToClientWithInstance.js +1 -0
  8. package/dist/cjs/runtime/addSchemaToClientWithInstance.js.map +1 -1
  9. package/dist/cjs/runtime/internals/APIClient.js +63 -42
  10. package/dist/cjs/runtime/internals/APIClient.js.map +1 -1
  11. package/dist/cjs/runtime/internals/cancellation.js +48 -0
  12. package/dist/cjs/runtime/internals/cancellation.js.map +1 -0
  13. package/dist/cjs/runtime/internals/clientUtils.js +45 -3
  14. package/dist/cjs/runtime/internals/clientUtils.js.map +1 -1
  15. package/dist/cjs/runtime/internals/index.js +3 -1
  16. package/dist/cjs/runtime/internals/index.js.map +1 -1
  17. package/dist/cjs/runtime/internals/operations/custom.js +91 -85
  18. package/dist/cjs/runtime/internals/operations/custom.js.map +1 -1
  19. package/dist/cjs/runtime/internals/operations/get.js +71 -65
  20. package/dist/cjs/runtime/internals/operations/get.js.map +1 -1
  21. package/dist/cjs/runtime/internals/operations/indexQuery.js +66 -60
  22. package/dist/cjs/runtime/internals/operations/indexQuery.js.map +1 -1
  23. package/dist/cjs/runtime/internals/operations/list.js +90 -84
  24. package/dist/cjs/runtime/internals/operations/list.js.map +1 -1
  25. package/dist/cjs/runtime/internals/server/generateModelsProperty.js +3 -3
  26. package/dist/cjs/runtime/internals/server/generateModelsProperty.js.map +1 -1
  27. package/dist/cjs/runtime/internals/utils/clientProperties/generateModelsProperty.js +3 -3
  28. package/dist/cjs/runtime/internals/utils/clientProperties/generateModelsProperty.js.map +1 -1
  29. package/dist/cjs/runtime/utils/index.js +3 -1
  30. package/dist/cjs/runtime/utils/index.js.map +1 -1
  31. package/dist/cjs/runtime/utils/selfAwareAsync.js +40 -0
  32. package/dist/cjs/runtime/utils/selfAwareAsync.js.map +1 -0
  33. package/dist/esm/Authorization.d.ts +1 -1
  34. package/dist/esm/ClientSchema/Core/ClientModel.d.ts +20 -1
  35. package/dist/esm/ClientSchema/utilities/ResolveField.d.ts +5 -1
  36. package/dist/esm/ModelType.d.ts +6 -0
  37. package/dist/esm/ModelType.mjs +5 -0
  38. package/dist/esm/ModelType.mjs.map +1 -1
  39. package/dist/esm/SchemaProcessor.mjs +58 -1
  40. package/dist/esm/SchemaProcessor.mjs.map +1 -1
  41. package/dist/esm/runtime/addSchemaToClient.mjs +2 -0
  42. package/dist/esm/runtime/addSchemaToClient.mjs.map +1 -1
  43. package/dist/esm/runtime/addSchemaToClientWithInstance.mjs +2 -0
  44. package/dist/esm/runtime/addSchemaToClientWithInstance.mjs.map +1 -1
  45. package/dist/esm/runtime/bridge-types.d.ts +2 -2
  46. package/dist/esm/runtime/client/index.d.ts +6 -6
  47. package/dist/esm/runtime/internals/APIClient.d.ts +2 -2
  48. package/dist/esm/runtime/internals/APIClient.mjs +64 -42
  49. package/dist/esm/runtime/internals/APIClient.mjs.map +1 -1
  50. package/dist/esm/runtime/internals/cancellation.d.ts +10 -0
  51. package/dist/esm/runtime/internals/cancellation.mjs +44 -0
  52. package/dist/esm/runtime/internals/cancellation.mjs.map +1 -0
  53. package/dist/esm/runtime/internals/clientUtils.d.ts +78 -1
  54. package/dist/esm/runtime/internals/clientUtils.mjs +45 -3
  55. package/dist/esm/runtime/internals/clientUtils.mjs.map +1 -1
  56. package/dist/esm/runtime/internals/index.d.ts +1 -0
  57. package/dist/esm/runtime/internals/index.mjs +1 -0
  58. package/dist/esm/runtime/internals/index.mjs.map +1 -1
  59. package/dist/esm/runtime/internals/operations/custom.d.ts +1 -1
  60. package/dist/esm/runtime/internals/operations/custom.mjs +91 -85
  61. package/dist/esm/runtime/internals/operations/custom.mjs.map +1 -1
  62. package/dist/esm/runtime/internals/operations/get.d.ts +1 -1
  63. package/dist/esm/runtime/internals/operations/get.mjs +71 -65
  64. package/dist/esm/runtime/internals/operations/get.mjs.map +1 -1
  65. package/dist/esm/runtime/internals/operations/indexQuery.d.ts +1 -1
  66. package/dist/esm/runtime/internals/operations/indexQuery.mjs +66 -60
  67. package/dist/esm/runtime/internals/operations/indexQuery.mjs.map +1 -1
  68. package/dist/esm/runtime/internals/operations/list.d.ts +1 -1
  69. package/dist/esm/runtime/internals/operations/list.mjs +90 -84
  70. package/dist/esm/runtime/internals/operations/list.mjs.map +1 -1
  71. package/dist/esm/runtime/internals/server/generateModelsProperty.mjs +4 -4
  72. package/dist/esm/runtime/internals/server/generateModelsProperty.mjs.map +1 -1
  73. package/dist/esm/runtime/internals/utils/clientProperties/generateModelsProperty.mjs +4 -4
  74. package/dist/esm/runtime/internals/utils/clientProperties/generateModelsProperty.mjs.map +1 -1
  75. package/dist/esm/runtime/utils/index.d.ts +1 -0
  76. package/dist/esm/runtime/utils/index.mjs +1 -0
  77. package/dist/esm/runtime/utils/index.mjs.map +1 -1
  78. package/dist/esm/runtime/utils/selfAwareAsync.d.ts +19 -0
  79. package/dist/esm/runtime/utils/selfAwareAsync.mjs +37 -0
  80. package/dist/esm/runtime/utils/selfAwareAsync.mjs.map +1 -0
  81. package/dist/meta/cjs.tsbuildinfo +1 -1
  82. package/package.json +2 -2
  83. package/src/ClientSchema/Core/ClientModel.ts +29 -0
  84. package/src/ClientSchema/utilities/ResolveField.ts +26 -6
  85. package/src/ModelType.ts +31 -0
  86. package/src/SchemaProcessor.ts +77 -2
  87. package/src/runtime/addSchemaToClient.ts +2 -0
  88. package/src/runtime/addSchemaToClientWithInstance.ts +2 -0
  89. package/src/runtime/bridge-types.ts +2 -2
  90. package/src/runtime/client/index.ts +207 -184
  91. package/src/runtime/internals/APIClient.ts +67 -23
  92. package/src/runtime/internals/cancellation.ts +57 -0
  93. package/src/runtime/internals/clientUtils.ts +61 -1
  94. package/src/runtime/internals/index.ts +1 -0
  95. package/src/runtime/internals/operations/custom.ts +117 -109
  96. package/src/runtime/internals/operations/get.ts +111 -95
  97. package/src/runtime/internals/operations/indexQuery.ts +100 -94
  98. package/src/runtime/internals/operations/list.ts +132 -119
  99. package/src/runtime/internals/server/generateModelsProperty.ts +31 -28
  100. package/src/runtime/internals/utils/clientProperties/generateModelsProperty.ts +36 -33
  101. package/src/runtime/utils/index.ts +1 -0
  102. package/src/runtime/utils/selfAwareAsync.ts +39 -0
@@ -1,11 +1,12 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
-
4
3
  import type {
5
4
  ModelAttribute,
6
5
  SchemaModel,
7
6
  SecondaryIndexAttribute,
7
+ ModelIntrospectionSchema,
8
8
  } from '../bridge-types';
9
+ import { graphQLOperationsInfo } from './APIClient';
9
10
 
10
11
  const attributeIsSecondaryIndex = (
11
12
  attr: ModelAttribute,
@@ -35,3 +36,62 @@ export const getSecondaryIndexesFromSchemaModel = (model: SchemaModel) => {
35
36
 
36
37
  return idxs || [];
37
38
  };
39
+
40
+ /**
41
+ * returns graphQLOperationsInfo, but filters out operations that were disabled via model().disableOperations([...])
42
+ */
43
+ export const excludeDisabledOps = (
44
+ mis: ModelIntrospectionSchema,
45
+ modelName: string,
46
+ ) => {
47
+ /* Example model attributes in MIS {
48
+ "type": "model",
49
+ "properties": {
50
+ "subscriptions": null,
51
+ "mutations": { "delete": null }
52
+ } }*/
53
+ const modelAttrs = mis.models[modelName].attributes?.find(
54
+ (attr) => attr.type === 'model',
55
+ );
56
+
57
+ const coarseToFineDict: Record<string, string[]> = {
58
+ queries: ['list', 'get', 'observeQuery'],
59
+ mutations: ['create', 'update', 'delete'],
60
+ subscriptions: ['onCreate', 'onUpdate', 'onDelete'],
61
+ };
62
+
63
+ const disabledOps: string[] = [];
64
+
65
+ if (!modelAttrs) {
66
+ return graphQLOperationsInfo;
67
+ }
68
+
69
+ if (modelAttrs.properties) {
70
+ for (const [key, value] of Object.entries(modelAttrs.properties)) {
71
+ if (value === null) {
72
+ // coarse-grained disable, e.g. "subscriptions": null,
73
+ disabledOps.push(...coarseToFineDict[key]);
74
+ } else if (value instanceof Object) {
75
+ // fine-grained, e.g. "mutations": { "delete": null }
76
+ disabledOps.push(...Object.keys(value));
77
+ }
78
+ }
79
+ }
80
+
81
+ // observeQuery only exists on the client side, so can't be explicitly disabled via schema builder.
82
+ // It's unusable without `list`
83
+ if (disabledOps.includes('list')) {
84
+ disabledOps.push('observeQuery');
85
+ }
86
+
87
+ // graphQLOperationsInfo keys are in caps
88
+ const disabledOpsUpper = disabledOps.map((op) => op.toUpperCase());
89
+
90
+ const filteredGraphQLOperations = Object.fromEntries(
91
+ Object.entries(graphQLOperationsInfo).filter(
92
+ ([key]) => !disabledOpsUpper.includes(key),
93
+ ),
94
+ );
95
+
96
+ return filteredGraphQLOperations;
97
+ };
@@ -10,3 +10,4 @@ export { generateModelsProperty } from './utils/clientProperties/generateModelsP
10
10
  export { isGraphQLResponseWithErrors } from './utils/runtimeTypeGuards/isGraphQLResponseWithErrors';
11
11
  export { isApiGraphQLConfig } from './utils/runtimeTypeGuards/isApiGraphQLProviderConfig';
12
12
  export { isConfigureEventWithResourceConfig } from './utils/runtimeTypeGuards/isConfigureEventWithResourceConfig';
13
+ export { upgradeClientCancellation } from './cancellation';
@@ -27,6 +27,9 @@ import {
27
27
  } from '../APIClient';
28
28
 
29
29
  import { handleSingularGraphQlError } from './utils';
30
+ import { selfAwareAsync } from '../../utils';
31
+
32
+ import { extendCancellability } from '../cancellation';
30
33
 
31
34
  type CustomOperationOptions = AuthModeParams & ListArgs;
32
35
 
@@ -360,7 +363,7 @@ function operationVariables(
360
363
  * @param context SSR context if relevant.
361
364
  * @returns Result from the graphql request, model-instantiated when relevant.
362
365
  */
363
- async function _op(
366
+ function _op(
364
367
  client: BaseClient,
365
368
  modelIntrospection: ModelIntrospectionSchema,
366
369
  operationType: 'query' | 'mutation',
@@ -370,114 +373,63 @@ async function _op(
370
373
  options?: AuthModeParams & ListArgs,
371
374
  context?: AmplifyServer.ContextSpec,
372
375
  ) {
373
- const { name: operationName } = operation;
374
- const auth = authModeParams(client, getInternals, options);
375
- const headers = getCustomHeaders(client, getInternals, options?.headers);
376
- const outerArgsString = outerArguments(operation);
377
- const innerArgsString = innerArguments(operation);
378
- const selectionSet = operationSelectionSet(modelIntrospection, operation);
379
-
380
- const returnTypeModelName = hasStringField(operation.type, 'model')
381
- ? operation.type.model
382
- : undefined;
383
-
384
- const query = `
376
+ return selfAwareAsync(async (resultPromise) => {
377
+ const { name: operationName } = operation;
378
+ const auth = authModeParams(client, getInternals, options);
379
+ const headers = getCustomHeaders(client, getInternals, options?.headers);
380
+ const outerArgsString = outerArguments(operation);
381
+ const innerArgsString = innerArguments(operation);
382
+ const selectionSet = operationSelectionSet(modelIntrospection, operation);
383
+
384
+ const returnTypeModelName = hasStringField(operation.type, 'model')
385
+ ? operation.type.model
386
+ : undefined;
387
+
388
+ const query = `
385
389
  ${operationType.toLocaleLowerCase()}${outerArgsString} {
386
390
  ${operationName}${innerArgsString} ${selectionSet}
387
391
  }
388
392
  `;
389
393
 
390
- const variables = operationVariables(operation, args);
394
+ const variables = operationVariables(operation, args);
395
+
396
+ try {
397
+ const basePromise = context
398
+ ? ((client as BaseSSRClient).graphql(
399
+ context,
400
+ {
401
+ ...auth,
402
+ query,
403
+ variables,
404
+ },
405
+ headers,
406
+ ) as Promise<GraphQLResult>)
407
+ : ((client as BaseBrowserClient).graphql(
408
+ {
409
+ ...auth,
410
+ query,
411
+ variables,
412
+ },
413
+ headers,
414
+ ) as Promise<GraphQLResult>);
415
+
416
+ const extendedPromise = extendCancellability(basePromise, resultPromise);
417
+ const { data, extensions } = await extendedPromise;
418
+
419
+ // flatten response
420
+ if (data) {
421
+ const [key] = Object.keys(data);
422
+
423
+ const isArrayResult = Array.isArray(data[key]);
424
+
425
+ // TODO: when adding support for custom selection set, flattening will need
426
+ // to occur recursively. For now, it's expected that related models are not
427
+ // present in the result. Only FK's are present. Any related model properties
428
+ // should be replaced with lazy loaders under the current implementation.
429
+ const flattenedResult = isArrayResult
430
+ ? data[key].filter((x: any) => x)
431
+ : data[key];
391
432
 
392
- try {
393
- const { data, extensions } = context
394
- ? ((await (client as BaseSSRClient).graphql(
395
- context,
396
- {
397
- ...auth,
398
- query,
399
- variables,
400
- },
401
- headers,
402
- )) as GraphQLResult)
403
- : ((await (client as BaseBrowserClient).graphql(
404
- {
405
- ...auth,
406
- query,
407
- variables,
408
- },
409
- headers,
410
- )) as GraphQLResult);
411
-
412
- // flatten response
413
- if (data) {
414
- const [key] = Object.keys(data);
415
-
416
- const isArrayResult = Array.isArray(data[key]);
417
-
418
- // TODO: when adding support for custom selection set, flattening will need
419
- // to occur recursively. For now, it's expected that related models are not
420
- // present in the result. Only FK's are present. Any related model properties
421
- // should be replaced with lazy loaders under the current implementation.
422
- const flattenedResult = isArrayResult
423
- ? data[key].filter((x: any) => x)
424
- : data[key];
425
-
426
- // TODO: custom selection set. current selection set is default selection set only
427
- // custom selection set requires data-schema-type + runtime updates above.
428
- const initialized = returnTypeModelName
429
- ? initializeModel(
430
- client,
431
- returnTypeModelName,
432
- isArrayResult ? flattenedResult : [flattenedResult],
433
- modelIntrospection,
434
- auth.authMode,
435
- auth.authToken,
436
- !!context,
437
- )
438
- : flattenedResult;
439
-
440
- return {
441
- data:
442
- !isArrayResult && Array.isArray(initialized)
443
- ? initialized.shift()
444
- : initialized,
445
- extensions,
446
- };
447
- } else {
448
- return { data: null, extensions };
449
- }
450
- } catch (error: any) {
451
- /**
452
- * The `data` type returned by `error` here could be:
453
- * 1) `null`
454
- * 2) an empty object
455
- * 3) "populated" but with a `null` value `{ getPost: null }`
456
- * 4) an actual record `{ getPost: { id: '1', title: 'Hello, World!' } }`
457
- */
458
- const { data, errors } = error;
459
-
460
- /**
461
- * `data` is not `null`, and is not an empty object:
462
- */
463
- if (data && Object.keys(data).length !== 0 && errors) {
464
- const [key] = Object.keys(data);
465
-
466
- const isArrayResult = Array.isArray(data[key]);
467
-
468
- // TODO: when adding support for custom selection set, flattening will need
469
- // to occur recursively. For now, it's expected that related models are not
470
- // present in the result. Only FK's are present. Any related model properties
471
- // should be replaced with lazy loaders under the current implementation.
472
- const flattenedResult = isArrayResult
473
- ? data[key].filter((x: any) => x)
474
- : data[key];
475
-
476
- /**
477
- * `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
478
- * if `flattenedResult`, result is an actual record:
479
- */
480
- if (flattenedResult) {
481
433
  // TODO: custom selection set. current selection set is default selection set only
482
434
  // custom selection set requires data-schema-type + runtime updates above.
483
435
  const initialized = returnTypeModelName
@@ -497,17 +449,73 @@ async function _op(
497
449
  !isArrayResult && Array.isArray(initialized)
498
450
  ? initialized.shift()
499
451
  : initialized,
500
- errors,
452
+ extensions,
501
453
  };
502
454
  } else {
503
- // was `data: { getPost: null }`)
455
+ return { data: null, extensions };
456
+ }
457
+ } catch (error: any) {
458
+ /**
459
+ * The `data` type returned by `error` here could be:
460
+ * 1) `null`
461
+ * 2) an empty object
462
+ * 3) "populated" but with a `null` value `{ getPost: null }`
463
+ * 4) an actual record `{ getPost: { id: '1', title: 'Hello, World!' } }`
464
+ */
465
+ const { data, errors } = error;
466
+
467
+ /**
468
+ * `data` is not `null`, and is not an empty object:
469
+ */
470
+ if (data && Object.keys(data).length !== 0 && errors) {
471
+ const [key] = Object.keys(data);
472
+
473
+ const isArrayResult = Array.isArray(data[key]);
474
+
475
+ // TODO: when adding support for custom selection set, flattening will need
476
+ // to occur recursively. For now, it's expected that related models are not
477
+ // present in the result. Only FK's are present. Any related model properties
478
+ // should be replaced with lazy loaders under the current implementation.
479
+ const flattenedResult = isArrayResult
480
+ ? data[key].filter((x: any) => x)
481
+ : data[key];
482
+
483
+ /**
484
+ * `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
485
+ * if `flattenedResult`, result is an actual record:
486
+ */
487
+ if (flattenedResult) {
488
+ // TODO: custom selection set. current selection set is default selection set only
489
+ // custom selection set requires data-schema-type + runtime updates above.
490
+ const initialized = returnTypeModelName
491
+ ? initializeModel(
492
+ client,
493
+ returnTypeModelName,
494
+ isArrayResult ? flattenedResult : [flattenedResult],
495
+ modelIntrospection,
496
+ auth.authMode,
497
+ auth.authToken,
498
+ !!context,
499
+ )
500
+ : flattenedResult;
501
+
502
+ return {
503
+ data:
504
+ !isArrayResult && Array.isArray(initialized)
505
+ ? initialized.shift()
506
+ : initialized,
507
+ errors,
508
+ };
509
+ } else {
510
+ // was `data: { getPost: null }`)
511
+ return handleSingularGraphQlError(error);
512
+ }
513
+ } else {
514
+ // `data` is `null`:
504
515
  return handleSingularGraphQlError(error);
505
516
  }
506
- } else {
507
- // `data` is `null`:
508
- return handleSingularGraphQlError(error);
509
517
  }
510
- }
518
+ });
511
519
  }
512
520
 
513
521
  /**
@@ -26,6 +26,9 @@ import {
26
26
  } from '../APIClient';
27
27
 
28
28
  import { handleSingularGraphQlError } from './utils';
29
+ import { selfAwareAsync } from '../../utils';
30
+
31
+ import { extendCancellability } from '../cancellation';
29
32
 
30
33
  export function getFactory(
31
34
  client: BaseClient,
@@ -35,7 +38,7 @@ export function getFactory(
35
38
  getInternals: ClientInternalsGetter,
36
39
  useContext = false,
37
40
  ) {
38
- const getWithContext = async (
41
+ const getWithContext = (
39
42
  contextSpec: AmplifyServer.ContextSpec & GraphQLOptions,
40
43
  arg?: any,
41
44
  options?: any,
@@ -52,7 +55,7 @@ export function getFactory(
52
55
  );
53
56
  };
54
57
 
55
- const get = async (arg?: any, options?: any) => {
58
+ const get = (arg?: any, options?: any) => {
56
59
  return _get(
57
60
  client,
58
61
  modelIntrospection,
@@ -67,7 +70,7 @@ export function getFactory(
67
70
  return useContext ? getWithContext : get;
68
71
  }
69
72
 
70
- async function _get(
73
+ function _get(
71
74
  client: BaseClient,
72
75
  modelIntrospection: ModelIntrospectionSchema,
73
76
  model: SchemaModel,
@@ -77,95 +80,62 @@ async function _get(
77
80
  getInternals: ClientInternalsGetter,
78
81
  context?: AmplifyServer.ContextSpec,
79
82
  ) {
80
- const { name } = model;
81
-
82
- const query = generateGraphQLDocument(
83
- modelIntrospection,
84
- name,
85
- operation,
86
- options,
87
- );
88
- const variables = buildGraphQLVariables(
89
- model,
90
- operation,
91
- arg,
92
- modelIntrospection,
93
- );
94
-
95
- const auth = authModeParams(client, getInternals, options);
96
-
97
- try {
98
- const headers = getCustomHeaders(client, getInternals, options?.headers);
99
-
100
- const { data, extensions } = context
101
- ? ((await (client as BaseSSRClient).graphql(
102
- context,
103
- {
104
- ...auth,
105
- query,
106
- variables,
107
- },
108
- headers,
109
- )) as GraphQLResult)
110
- : ((await (client as BaseBrowserClient).graphql(
111
- {
112
- ...auth,
113
- query,
114
- variables,
115
- },
116
- headers,
117
- )) as GraphQLResult);
118
-
119
- // flatten response
120
- if (data) {
121
- const [key] = Object.keys(data);
122
- const flattenedResult = flattenItems(modelIntrospection, name, data[key]);
123
-
124
- if (flattenedResult === null) {
125
- return { data: null, extensions };
126
- } else if (options?.selectionSet) {
127
- return { data: flattenedResult, extensions };
128
- } else {
129
- // TODO: refactor to avoid destructuring here
130
- const [initialized] = initializeModel(
131
- client,
132
- name,
133
- [flattenedResult],
83
+ return selfAwareAsync(async (resultPromise) => {
84
+ const { name } = model;
85
+
86
+ const query = generateGraphQLDocument(
87
+ modelIntrospection,
88
+ name,
89
+ operation,
90
+ options,
91
+ );
92
+ const variables = buildGraphQLVariables(
93
+ model,
94
+ operation,
95
+ arg,
96
+ modelIntrospection,
97
+ );
98
+
99
+ const auth = authModeParams(client, getInternals, options);
100
+
101
+ try {
102
+ const headers = getCustomHeaders(client, getInternals, options?.headers);
103
+
104
+ const basePromise = context
105
+ ? ((client as BaseSSRClient).graphql(
106
+ context,
107
+ {
108
+ ...auth,
109
+ query,
110
+ variables,
111
+ },
112
+ headers,
113
+ ) as Promise<GraphQLResult>)
114
+ : ((client as BaseBrowserClient).graphql(
115
+ {
116
+ ...auth,
117
+ query,
118
+ variables,
119
+ },
120
+ headers,
121
+ ) as Promise<GraphQLResult>);
122
+
123
+ const extendedPromise = extendCancellability(basePromise, resultPromise);
124
+ const { data, extensions } = await extendedPromise;
125
+
126
+ // flatten response
127
+ if (data) {
128
+ const [key] = Object.keys(data);
129
+ const flattenedResult = flattenItems(
134
130
  modelIntrospection,
135
- auth.authMode,
136
- auth.authToken,
137
- !!context,
131
+ name,
132
+ data[key],
138
133
  );
139
134
 
140
- return { data: initialized, extensions };
141
- }
142
- } else {
143
- return { data: null, extensions };
144
- }
145
- } catch (error: any) {
146
- /**
147
- * The `data` type returned by `error` here could be:
148
- * 1) `null`
149
- * 2) an empty object
150
- * 3) "populated" but with a `null` value `{ getPost: null }`
151
- * 4) an actual record `{ getPost: { id: '1', title: 'Hello, World!' } }`
152
- */
153
- const { data, errors } = error;
154
-
155
- /**
156
- * `data` is not `null`, and is not an empty object:
157
- */
158
- if (data && Object.keys(data).length !== 0 && errors) {
159
- const [key] = Object.keys(data);
160
- const flattenedResult = flattenItems(modelIntrospection, name, data[key]);
161
-
162
- /**
163
- * `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
164
- * if `flattenedResult`, result is an actual record:
165
- */
166
- if (flattenedResult) {
167
- if (options?.selectionSet) {
168
- return { data: flattenedResult, errors };
135
+ if (flattenedResult === null) {
136
+ return { data: null, extensions };
137
+ } else if (options?.selectionSet) {
138
+ return { data: flattenedResult, extensions };
169
139
  } else {
170
140
  // TODO: refactor to avoid destructuring here
171
141
  const [initialized] = initializeModel(
@@ -178,15 +148,61 @@ async function _get(
178
148
  !!context,
179
149
  );
180
150
 
181
- return { data: initialized, errors };
151
+ return { data: initialized, extensions };
152
+ }
153
+ } else {
154
+ return { data: null, extensions };
155
+ }
156
+ } catch (error: any) {
157
+ /**
158
+ * The `data` type returned by `error` here could be:
159
+ * 1) `null`
160
+ * 2) an empty object
161
+ * 3) "populated" but with a `null` value `{ getPost: null }`
162
+ * 4) an actual record `{ getPost: { id: '1', title: 'Hello, World!' } }`
163
+ */
164
+ const { data, errors } = error;
165
+
166
+ /**
167
+ * `data` is not `null`, and is not an empty object:
168
+ */
169
+ if (data && Object.keys(data).length !== 0 && errors) {
170
+ const [key] = Object.keys(data);
171
+ const flattenedResult = flattenItems(
172
+ modelIntrospection,
173
+ name,
174
+ data[key],
175
+ );
176
+
177
+ /**
178
+ * `flattenedResult` could be `null` here (e.g. `data: { getPost: null }`)
179
+ * if `flattenedResult`, result is an actual record:
180
+ */
181
+ if (flattenedResult) {
182
+ if (options?.selectionSet) {
183
+ return { data: flattenedResult, errors };
184
+ } else {
185
+ // TODO: refactor to avoid destructuring here
186
+ const [initialized] = initializeModel(
187
+ client,
188
+ name,
189
+ [flattenedResult],
190
+ modelIntrospection,
191
+ auth.authMode,
192
+ auth.authToken,
193
+ !!context,
194
+ );
195
+
196
+ return { data: initialized, errors };
197
+ }
198
+ } else {
199
+ // was `data: { getPost: null }`)
200
+ return handleSingularGraphQlError(error);
182
201
  }
183
202
  } else {
184
- // was `data: { getPost: null }`)
203
+ // `data` is `null`:
185
204
  return handleSingularGraphQlError(error);
186
205
  }
187
- } else {
188
- // `data` is `null`:
189
- return handleSingularGraphQlError(error);
190
206
  }
191
- }
207
+ });
192
208
  }