@isograph/react 0.4.3 → 0.5.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 (137) hide show
  1. package/.turbo/turbo-compile-libs.log +2 -2
  2. package/dist/core/FragmentReference.d.ts +4 -2
  3. package/dist/core/FragmentReference.d.ts.map +1 -1
  4. package/dist/core/FragmentReference.js +2 -2
  5. package/dist/core/IsographEnvironment.d.ts +19 -11
  6. package/dist/core/IsographEnvironment.d.ts.map +1 -1
  7. package/dist/core/IsographEnvironment.js +27 -2
  8. package/dist/core/PromiseWrapper.d.ts +13 -7
  9. package/dist/core/PromiseWrapper.d.ts.map +1 -1
  10. package/dist/core/brand.d.ts +17 -0
  11. package/dist/core/brand.d.ts.map +1 -1
  12. package/dist/core/cache.d.ts +10 -7
  13. package/dist/core/cache.d.ts.map +1 -1
  14. package/dist/core/cache.js +102 -74
  15. package/dist/core/check.d.ts +8 -4
  16. package/dist/core/check.d.ts.map +1 -1
  17. package/dist/core/check.js +10 -7
  18. package/dist/core/componentCache.d.ts +1 -1
  19. package/dist/core/componentCache.d.ts.map +1 -1
  20. package/dist/core/componentCache.js +6 -4
  21. package/dist/core/entrypoint.d.ts +17 -7
  22. package/dist/core/entrypoint.d.ts.map +1 -1
  23. package/dist/core/garbageCollection.d.ts +8 -2
  24. package/dist/core/garbageCollection.d.ts.map +1 -1
  25. package/dist/core/garbageCollection.js +36 -14
  26. package/dist/core/logging.d.ts +16 -3
  27. package/dist/core/logging.d.ts.map +1 -1
  28. package/dist/core/makeNetworkRequest.d.ts +4 -2
  29. package/dist/core/makeNetworkRequest.d.ts.map +1 -1
  30. package/dist/core/makeNetworkRequest.js +115 -38
  31. package/dist/core/optimisticProxy.d.ts +59 -0
  32. package/dist/core/optimisticProxy.d.ts.map +1 -0
  33. package/dist/core/optimisticProxy.js +399 -0
  34. package/dist/core/read.d.ts +3 -2
  35. package/dist/core/read.d.ts.map +1 -1
  36. package/dist/core/read.js +158 -123
  37. package/dist/core/reader.d.ts +7 -4
  38. package/dist/core/reader.d.ts.map +1 -1
  39. package/dist/core/startUpdate.d.ts +3 -2
  40. package/dist/core/startUpdate.d.ts.map +1 -1
  41. package/dist/core/startUpdate.js +33 -34
  42. package/dist/index.d.ts +2 -2
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +2 -1
  45. package/dist/loadable-hooks/useClientSideDefer.d.ts +9 -4
  46. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -1
  47. package/dist/loadable-hooks/useClientSideDefer.js +34 -1
  48. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +5 -3
  49. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -1
  50. package/dist/loadable-hooks/useConnectionSpecPagination.js +27 -13
  51. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +1 -1
  52. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -1
  53. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +1 -1
  54. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -1
  55. package/dist/loadable-hooks/useSkipLimitPagination.js +1 -1
  56. package/dist/react/FragmentReader.d.ts +2 -1
  57. package/dist/react/FragmentReader.d.ts.map +1 -1
  58. package/dist/react/FragmentRenderer.d.ts +2 -1
  59. package/dist/react/FragmentRenderer.d.ts.map +1 -1
  60. package/dist/react/LoadableFieldReader.d.ts +9 -3
  61. package/dist/react/LoadableFieldReader.d.ts.map +1 -1
  62. package/dist/react/LoadableFieldReader.js +40 -1
  63. package/dist/react/LoadableFieldRenderer.d.ts +9 -3
  64. package/dist/react/LoadableFieldRenderer.d.ts.map +1 -1
  65. package/dist/react/LoadableFieldRenderer.js +36 -1
  66. package/dist/react/useImperativeReference.d.ts +4 -3
  67. package/dist/react/useImperativeReference.d.ts.map +1 -1
  68. package/dist/react/useImperativeReference.js +3 -5
  69. package/dist/react/useLazyReference.d.ts +2 -1
  70. package/dist/react/useLazyReference.d.ts.map +1 -1
  71. package/dist/react/useReadAndSubscribe.d.ts.map +1 -1
  72. package/dist/react/useReadAndSubscribe.js +1 -3
  73. package/dist/react/useResult.d.ts.map +1 -1
  74. package/dist/react/useResult.js +6 -5
  75. package/package.json +16 -17
  76. package/src/core/FragmentReference.ts +10 -4
  77. package/src/core/IsographEnvironment.ts +59 -13
  78. package/src/core/PromiseWrapper.ts +14 -7
  79. package/src/core/brand.ts +18 -0
  80. package/src/core/cache.ts +186 -91
  81. package/src/core/check.ts +21 -10
  82. package/src/core/componentCache.ts +8 -4
  83. package/src/core/entrypoint.ts +35 -6
  84. package/src/core/garbageCollection.ts +61 -19
  85. package/src/core/logging.ts +15 -3
  86. package/src/core/makeNetworkRequest.ts +307 -74
  87. package/src/core/optimisticProxy.ts +563 -0
  88. package/src/core/read.ts +233 -163
  89. package/src/core/reader.ts +10 -6
  90. package/src/core/startUpdate.ts +45 -30
  91. package/src/index.ts +2 -1
  92. package/src/loadable-hooks/useClientSideDefer.ts +76 -26
  93. package/src/loadable-hooks/useConnectionSpecPagination.ts +34 -17
  94. package/src/loadable-hooks/useImperativeLoadableField.ts +2 -2
  95. package/src/loadable-hooks/useSkipLimitPagination.ts +2 -3
  96. package/src/react/FragmentReader.tsx +3 -1
  97. package/src/react/FragmentRenderer.tsx +8 -1
  98. package/src/react/LoadableFieldReader.tsx +123 -12
  99. package/src/react/LoadableFieldRenderer.tsx +122 -12
  100. package/src/react/useImperativeReference.ts +20 -11
  101. package/src/react/useLazyReference.ts +17 -6
  102. package/src/react/useReadAndSubscribe.ts +1 -8
  103. package/src/react/useResult.ts +9 -11
  104. package/src/tests/__isograph/Node/asEconomist/resolver_reader.ts +1 -1
  105. package/src/tests/__isograph/Query/linkedUpdate/entrypoint.ts +3 -1
  106. package/src/tests/__isograph/Query/linkedUpdate/raw_response_type.ts +13 -0
  107. package/src/tests/__isograph/Query/linkedUpdate/resolver_reader.ts +1 -1
  108. package/src/tests/__isograph/Query/meName/entrypoint.ts +3 -1
  109. package/src/tests/__isograph/Query/meName/raw_response_type.ts +7 -0
  110. package/src/tests/__isograph/Query/meName/resolver_reader.ts +1 -1
  111. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +3 -1
  112. package/src/tests/__isograph/Query/meNameSuccessor/raw_response_type.ts +14 -0
  113. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +1 -1
  114. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +3 -1
  115. package/src/tests/__isograph/Query/nodeField/raw_response_type.ts +7 -0
  116. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +1 -1
  117. package/src/tests/__isograph/Query/normalizeUndefinedField/entrypoint.ts +33 -0
  118. package/src/tests/__isograph/Query/normalizeUndefinedField/normalization_ast.ts +25 -0
  119. package/src/tests/__isograph/Query/normalizeUndefinedField/output_type.ts +3 -0
  120. package/src/tests/__isograph/Query/normalizeUndefinedField/param_type.ts +9 -0
  121. package/src/tests/__isograph/Query/normalizeUndefinedField/query_text.ts +6 -0
  122. package/src/tests/__isograph/Query/normalizeUndefinedField/raw_response_type.ts +7 -0
  123. package/src/tests/__isograph/Query/normalizeUndefinedField/resolver_reader.ts +38 -0
  124. package/src/tests/__isograph/Query/startUpdate/entrypoint.ts +3 -1
  125. package/src/tests/__isograph/Query/startUpdate/raw_response_type.ts +8 -0
  126. package/src/tests/__isograph/Query/startUpdate/resolver_reader.ts +1 -1
  127. package/src/tests/__isograph/Query/subquery/entrypoint.ts +3 -1
  128. package/src/tests/__isograph/Query/subquery/raw_response_type.ts +9 -0
  129. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +1 -1
  130. package/src/tests/__isograph/iso.ts +11 -1
  131. package/src/tests/garbageCollection.test.ts +8 -5
  132. package/src/tests/meNameSuccessor.ts +6 -3
  133. package/src/tests/nodeQuery.ts +4 -2
  134. package/src/tests/normalizeData.test.ts +89 -15
  135. package/src/tests/optimisticProxy.test.ts +860 -0
  136. package/src/tests/startUpdate.test.ts +7 -5
  137. package/src/tests/__isograph/Economist/__link/output_type.ts +0 -2
package/src/core/read.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  import {
22
22
  assertLink,
23
23
  getOrLoadIsographArtifact,
24
+ getOrLoadReaderWithRefetchQueries,
24
25
  IsographEnvironment,
25
26
  type DataTypeValue,
26
27
  type StoreLink,
@@ -28,6 +29,7 @@ import {
28
29
  } from './IsographEnvironment';
29
30
  import { logMessage } from './logging';
30
31
  import { maybeMakeNetworkRequest } from './makeNetworkRequest';
32
+ import { getStoreRecordProxy } from './optimisticProxy';
31
33
  import {
32
34
  getPromiseState,
33
35
  NOT_SET,
@@ -39,6 +41,7 @@ import {
39
41
  import {
40
42
  ReaderAst,
41
43
  type LoadablySelectedField,
44
+ type ReaderClientPointer,
42
45
  type ReaderImperativelyLoadedField,
43
46
  type ReaderLinkedField,
44
47
  type ReaderNonLoadableResolverField,
@@ -152,7 +155,7 @@ function readData<TReadFromStore>(
152
155
  root.__typename,
153
156
  );
154
157
  encounteredIds.add(root.__link);
155
- let storeRecord = environment.store[root.__typename]?.[root.__link];
158
+ let storeRecord = getStoreRecordProxy(environment.store, root);
156
159
  if (storeRecord === undefined) {
157
160
  return {
158
161
  kind: 'MissingData',
@@ -313,7 +316,7 @@ export function readLoadablySelectedFieldData(
313
316
  data: (
314
317
  args: any,
315
318
  // TODO get the associated type for FetchOptions from the loadably selected field
316
- fetchOptions?: FetchOptions<any>,
319
+ fetchOptions?: FetchOptions<any, never>,
317
320
  ) => {
318
321
  // TODO we should use the reader AST for this
319
322
  const includeReadOutData = (variables: any, readOutData: any) => {
@@ -342,13 +345,13 @@ export function readLoadablySelectedFieldData(
342
345
  // Fetcher
343
346
  () => {
344
347
  const fragmentReferenceAndDisposeFromEntrypoint = (
345
- entrypoint: IsographEntrypoint<any, any, any>,
348
+ entrypoint: IsographEntrypoint<any, any, any, {}>,
346
349
  ): [FragmentReference<any, any>, CleanupFn] => {
347
- const readerWithRefetchQueries =
348
- entrypoint.readerWithRefetchQueries.kind ===
349
- 'ReaderWithRefetchQueriesLoader'
350
- ? wrapPromise(entrypoint.readerWithRefetchQueries.loader())
351
- : wrapResolvedValue(entrypoint.readerWithRefetchQueries);
350
+ const { fieldName, readerArtifactKind, readerWithRefetchQueries } =
351
+ getOrLoadReaderWithRefetchQueries(
352
+ environment,
353
+ entrypoint.readerWithRefetchQueries,
354
+ );
352
355
  const [networkRequest, disposeNetworkRequest] =
353
356
  maybeMakeNetworkRequest(
354
357
  environment,
@@ -361,7 +364,8 @@ export function readLoadablySelectedFieldData(
361
364
  const fragmentReference: FragmentReference<any, any> = {
362
365
  kind: 'FragmentReference',
363
366
  readerWithRefetchQueries,
364
-
367
+ fieldName,
368
+ readerArtifactKind,
365
369
  // TODO localVariables is not guaranteed to have an id field
366
370
  root,
367
371
  variables: localVariables,
@@ -395,11 +399,12 @@ export function readLoadablySelectedFieldData(
395
399
  | { kind: 'Disposed' } = { kind: 'EntrypointNotLoaded' };
396
400
 
397
401
  const readerWithRefetchQueries = wrapPromise(
398
- isographArtifactPromiseWrapper.promise.then((entrypoint) =>
399
- entrypoint.readerWithRefetchQueries.kind ===
400
- 'ReaderWithRefetchQueriesLoader'
401
- ? entrypoint.readerWithRefetchQueries.loader()
402
- : entrypoint.readerWithRefetchQueries,
402
+ isographArtifactPromiseWrapper.promise.then(
403
+ (entrypoint) =>
404
+ getOrLoadReaderWithRefetchQueries(
405
+ environment,
406
+ entrypoint.readerWithRefetchQueries,
407
+ ).readerWithRefetchQueries.promise,
403
408
  ),
404
409
  );
405
410
  const networkRequest = wrapPromise(
@@ -425,7 +430,8 @@ export function readLoadablySelectedFieldData(
425
430
  const fragmentReference: FragmentReference<any, any> = {
426
431
  kind: 'FragmentReference',
427
432
  readerWithRefetchQueries,
428
-
433
+ fieldName: field.name,
434
+ readerArtifactKind: field.entrypoint.readerArtifactKind,
429
435
  // TODO localVariables is not guaranteed to have an id field
430
436
  root,
431
437
  variables: localVariables,
@@ -561,6 +567,8 @@ export function readResolverFieldData(
561
567
  const fragment = {
562
568
  kind: 'FragmentReference',
563
569
  readerWithRefetchQueries: wrapResolvedValue(readerWithRefetchQueries),
570
+ fieldName: field.readerArtifact.fieldName,
571
+ readerArtifactKind: field.readerArtifact.kind,
564
572
  root,
565
573
  variables: generateChildVariableMap(variables, field.arguments),
566
574
  networkRequest,
@@ -593,7 +601,6 @@ export function readResolverFieldData(
593
601
  ? getOrCreateCachedStartUpdate(
594
602
  environment,
595
603
  fragment,
596
- readerWithRefetchQueries.readerArtifact.fieldName,
597
604
  networkRequestOptions,
598
605
  )
599
606
  : undefined,
@@ -608,7 +615,6 @@ export function readResolverFieldData(
608
615
  kind: 'Success',
609
616
  data: getOrCreateCachedComponent(
610
617
  environment,
611
- field.readerArtifact.fieldName,
612
618
  fragment,
613
619
  networkRequestOptions,
614
620
  ),
@@ -628,7 +634,7 @@ export function readScalarFieldData(
628
634
  root: StoreLink,
629
635
  variables: Variables,
630
636
  ): ReadDataResult<
631
- string | number | boolean | StoreLink | DataTypeValue[] | null
637
+ string | number | boolean | StoreLink | readonly DataTypeValue[] | null
632
638
  > {
633
639
  const storeRecordName = getParentRecordKey(field, variables);
634
640
  const value = storeRecord[storeRecordName];
@@ -659,51 +665,7 @@ export function readLinkedFieldData(
659
665
  ) => ReadDataResult<object>,
660
666
  ): ReadDataResult<unknown> {
661
667
  const storeRecordName = getParentRecordKey(field, variables);
662
- const value = storeRecord[storeRecordName];
663
- if (Array.isArray(value)) {
664
- const results = [];
665
- for (const item of value) {
666
- const link = assertLink(item);
667
- if (link === undefined) {
668
- return {
669
- kind: 'MissingData',
670
- reason:
671
- 'No link for ' +
672
- storeRecordName +
673
- ' on root ' +
674
- root.__link +
675
- '. Link is ' +
676
- JSON.stringify(item),
677
- recordLink: root,
678
- };
679
- } else if (link === null) {
680
- results.push(null);
681
- continue;
682
- }
683
-
684
- const result = readData(field.selections, link);
685
- if (result.kind === 'MissingData') {
686
- return {
687
- kind: 'MissingData',
688
- reason:
689
- 'Missing data for ' +
690
- storeRecordName +
691
- ' on root ' +
692
- root.__link +
693
- '. Link is ' +
694
- JSON.stringify(item),
695
- nestedReason: result,
696
- recordLink: result.recordLink,
697
- };
698
- }
699
- results.push(result.data);
700
- }
701
- return {
702
- kind: 'Success',
703
- data: results,
704
- };
705
- }
706
- let link = assertLink(value);
668
+ let value = storeRecord[storeRecordName];
707
669
 
708
670
  if (field.condition) {
709
671
  const data = readData(field.condition.readerAst, root);
@@ -730,6 +692,8 @@ export function readLinkedFieldData(
730
692
  kind: 'FragmentReference',
731
693
  readerWithRefetchQueries: wrapResolvedValue(readerWithRefetchQueries),
732
694
  root,
695
+ fieldName: field.condition.fieldName,
696
+ readerArtifactKind: field.condition.kind,
733
697
  variables: generateChildVariableMap(
734
698
  variables,
735
699
  // TODO this is wrong
@@ -748,14 +712,85 @@ export function readLinkedFieldData(
748
712
  startUpdate: getOrCreateCachedStartUpdate(
749
713
  environment,
750
714
  fragment,
751
- readerWithRefetchQueries.readerArtifact.fieldName,
752
715
  networkRequestOptions,
753
716
  ),
754
717
  }
755
718
  : undefined),
756
719
  });
757
- link = condition;
720
+ value = condition;
721
+ }
722
+
723
+ if (Array.isArray(value)) {
724
+ const results = [];
725
+ for (const item of value) {
726
+ const link = assertLink(item);
727
+ if (link === undefined) {
728
+ return {
729
+ kind: 'MissingData',
730
+ reason:
731
+ 'No link for ' +
732
+ storeRecordName +
733
+ ' on root ' +
734
+ root.__link +
735
+ '. Link is ' +
736
+ JSON.stringify(item),
737
+ recordLink: root,
738
+ };
739
+ } else if (link === null) {
740
+ results.push(null);
741
+ continue;
742
+ }
743
+
744
+ if (isClientPointer(field)) {
745
+ const result = readClientPointerData(
746
+ environment,
747
+ field,
748
+ link,
749
+ variables,
750
+ nestedRefetchQueries,
751
+ readData,
752
+ );
753
+ if (result.kind === 'MissingData') {
754
+ return {
755
+ kind: 'MissingData',
756
+ reason:
757
+ 'Missing data for ' +
758
+ storeRecordName +
759
+ ' on root ' +
760
+ root.__link +
761
+ '. Link is ' +
762
+ JSON.stringify(item),
763
+ nestedReason: result,
764
+ recordLink: result.recordLink,
765
+ };
766
+ }
767
+ results.push(result.data);
768
+ continue;
769
+ }
770
+
771
+ const result = readData(field.selections, link);
772
+ if (result.kind === 'MissingData') {
773
+ return {
774
+ kind: 'MissingData',
775
+ reason:
776
+ 'Missing data for ' +
777
+ storeRecordName +
778
+ ' on root ' +
779
+ root.__link +
780
+ '. Link is ' +
781
+ JSON.stringify(item),
782
+ nestedReason: result,
783
+ recordLink: result.recordLink,
784
+ };
785
+ }
786
+ results.push(result.data);
787
+ }
788
+ return {
789
+ kind: 'Success',
790
+ data: results,
791
+ };
758
792
  }
793
+ let link = assertLink(value);
759
794
 
760
795
  if (link === undefined) {
761
796
  // TODO make this configurable, and also generated and derived from the schema
@@ -798,111 +833,28 @@ export function readLinkedFieldData(
798
833
  data: null,
799
834
  };
800
835
  }
801
- const targetId = link;
802
- const { refetchQueryIndex } = field;
803
- if (refetchQueryIndex != null) {
804
- // if field.refetchQueryIndex is not null, then the field is a client pointer, i.e.
805
- // it is like a loadable field that returns the selections.
806
- const refetchReaderParams = readData(
807
- [
808
- {
809
- kind: 'Scalar',
810
- fieldName: 'id',
811
- alias: null,
812
- arguments: null,
813
- isUpdatable: false,
814
- },
815
- ],
816
- targetId,
817
- );
818
836
 
819
- if (refetchReaderParams.kind === 'MissingData') {
837
+ if (isClientPointer(field)) {
838
+ const data = readClientPointerData(
839
+ environment,
840
+ field,
841
+ link,
842
+ variables,
843
+ nestedRefetchQueries,
844
+ readData,
845
+ );
846
+ if (data.kind === 'MissingData') {
820
847
  return {
821
848
  kind: 'MissingData',
822
849
  reason:
823
- 'Missing data for ' + field.alias + ' on root ' + targetId.__link,
824
- nestedReason: refetchReaderParams,
825
- recordLink: refetchReaderParams.recordLink,
850
+ 'Missing data for ' + storeRecordName + ' on root ' + root.__link,
851
+ nestedReason: data,
852
+ recordLink: data.recordLink,
826
853
  };
827
854
  }
828
-
829
- const refetchQuery = nestedRefetchQueries[refetchQueryIndex];
830
- if (refetchQuery == null) {
831
- throw new Error(
832
- 'refetchQuery is null in RefetchField. This is indicative of a bug in Isograph.',
833
- );
834
- }
835
- const refetchQueryArtifact = refetchQuery.artifact;
836
- const allowedVariables = refetchQuery.allowedVariables;
837
-
838
- return {
839
- kind: 'Success',
840
- data: (
841
- args: any,
842
- // TODO get the associated type for FetchOptions from the loadably selected field
843
- fetchOptions?: FetchOptions<any>,
844
- ) => {
845
- const includeReadOutData = (variables: any, readOutData: any) => {
846
- variables.id = readOutData.id;
847
- return variables;
848
- };
849
- const localVariables = includeReadOutData(
850
- args ?? {},
851
- refetchReaderParams.data,
852
- );
853
- writeQueryArgsToVariables(localVariables, field.arguments, variables);
854
-
855
- return [
856
- // Stable id
857
- targetId.__typename +
858
- ':' +
859
- targetId.__link +
860
- '/' +
861
- field.fieldName +
862
- '/' +
863
- stableStringifyArgs(localVariables),
864
- // Fetcher
865
- (): ItemCleanupPair<FragmentReference<any, any>> | undefined => {
866
- const variables = includeReadOutData(
867
- filterVariables({ ...args, ...localVariables }, allowedVariables),
868
- refetchReaderParams.data,
869
- );
870
-
871
- const readerWithRefetchQueries = wrapResolvedValue({
872
- kind: 'ReaderWithRefetchQueries',
873
- readerArtifact: {
874
- kind: 'EagerReaderArtifact',
875
- fieldName: field.fieldName,
876
- readerAst: field.selections,
877
- resolver: ({ data }: { data: any }) => data,
878
- hasUpdatable: false,
879
- },
880
- nestedRefetchQueries,
881
- } as const);
882
-
883
- const [networkRequest, disposeNetworkRequest] =
884
- maybeMakeNetworkRequest(
885
- environment,
886
- refetchQueryArtifact,
887
- variables,
888
- readerWithRefetchQueries,
889
- fetchOptions ?? null,
890
- );
891
-
892
- const fragmentReference: FragmentReference<any, any> = {
893
- kind: 'FragmentReference',
894
- readerWithRefetchQueries: readerWithRefetchQueries,
895
- root: targetId,
896
- variables,
897
- networkRequest,
898
- };
899
- return [fragmentReference, disposeNetworkRequest];
900
- },
901
- ];
902
- },
903
- };
855
+ return data;
904
856
  }
905
- const data = readData(field.selections, targetId);
857
+ const data = readData(field.selections, link);
906
858
  if (data.kind === 'MissingData') {
907
859
  return {
908
860
  kind: 'MissingData',
@@ -914,6 +866,124 @@ export function readLinkedFieldData(
914
866
  return data;
915
867
  }
916
868
 
869
+ function isClientPointer(
870
+ field: ReaderLinkedField,
871
+ ): field is ReaderClientPointer {
872
+ return field.refetchQueryIndex !== null;
873
+ }
874
+
875
+ export function readClientPointerData(
876
+ environment: IsographEnvironment,
877
+ field: ReaderClientPointer,
878
+ root: StoreLink,
879
+ variables: Variables,
880
+ nestedRefetchQueries: RefetchQueryNormalizationArtifactWrapper[],
881
+ readData: <TReadFromStore>(
882
+ ast: ReaderAst<TReadFromStore>,
883
+ root: StoreLink,
884
+ ) => ReadDataResult<object>,
885
+ ): ReadDataResult<unknown> {
886
+ const refetchReaderParams = readData(
887
+ [
888
+ {
889
+ kind: 'Scalar',
890
+ fieldName: 'id',
891
+ alias: null,
892
+ arguments: null,
893
+ isUpdatable: false,
894
+ },
895
+ ],
896
+ root,
897
+ );
898
+
899
+ if (refetchReaderParams.kind === 'MissingData') {
900
+ return {
901
+ kind: 'MissingData',
902
+ reason: 'Missing data for ' + field.alias + ' on root ' + root.__link,
903
+ nestedReason: refetchReaderParams,
904
+ recordLink: refetchReaderParams.recordLink,
905
+ };
906
+ }
907
+
908
+ const refetchQuery = nestedRefetchQueries[field.refetchQueryIndex];
909
+ if (refetchQuery == null) {
910
+ throw new Error(
911
+ 'refetchQuery is null in RefetchField. This is indicative of a bug in Isograph.',
912
+ );
913
+ }
914
+ const refetchQueryArtifact = refetchQuery.artifact;
915
+ const allowedVariables = refetchQuery.allowedVariables;
916
+
917
+ return {
918
+ kind: 'Success',
919
+ data: (
920
+ args: any,
921
+ // TODO get the associated type for FetchOptions from the loadably selected field
922
+ fetchOptions?: FetchOptions<any, never>,
923
+ ) => {
924
+ const includeReadOutData = (variables: any, readOutData: any) => {
925
+ variables.id = readOutData.id;
926
+ return variables;
927
+ };
928
+ const localVariables = includeReadOutData(
929
+ args ?? {},
930
+ refetchReaderParams.data,
931
+ );
932
+ writeQueryArgsToVariables(localVariables, field.arguments, variables);
933
+
934
+ return [
935
+ // Stable id
936
+ root.__typename +
937
+ ':' +
938
+ root.__link +
939
+ '/' +
940
+ field.fieldName +
941
+ '/' +
942
+ stableStringifyArgs(localVariables),
943
+ // Fetcher
944
+ (): ItemCleanupPair<FragmentReference<any, any>> | undefined => {
945
+ const variables = includeReadOutData(
946
+ filterVariables({ ...args, ...localVariables }, allowedVariables),
947
+ refetchReaderParams.data,
948
+ );
949
+
950
+ const readerWithRefetchQueries = wrapResolvedValue({
951
+ kind: 'ReaderWithRefetchQueries',
952
+ readerArtifact: {
953
+ kind: 'EagerReaderArtifact',
954
+ fieldName: field.fieldName,
955
+ readerAst: field.selections,
956
+ resolver: ({ data }: { data: any }) => data,
957
+ hasUpdatable: false,
958
+ },
959
+ nestedRefetchQueries,
960
+ } as const);
961
+
962
+ const [networkRequest, disposeNetworkRequest] =
963
+ maybeMakeNetworkRequest(
964
+ environment,
965
+ refetchQueryArtifact,
966
+ variables,
967
+ readerWithRefetchQueries,
968
+ fetchOptions ?? null,
969
+ );
970
+
971
+ const fragmentReference: FragmentReference<any, any> = {
972
+ kind: 'FragmentReference',
973
+ fieldName: field.fieldName,
974
+ readerArtifactKind: 'EagerReaderArtifact',
975
+ readerWithRefetchQueries: readerWithRefetchQueries,
976
+ root,
977
+ variables,
978
+ networkRequest,
979
+ };
980
+ return [fragmentReference, disposeNetworkRequest];
981
+ },
982
+ ];
983
+ },
984
+ };
985
+ }
986
+
917
987
  export type NetworkRequestReaderOptions = {
918
988
  suspendIfInFlight: boolean;
919
989
  throwOnNetworkError: boolean;
@@ -31,7 +31,7 @@ export type EagerReaderArtifact<
31
31
  TClientFieldValue,
32
32
  > = {
33
33
  readonly kind: 'EagerReaderArtifact';
34
- readonly fieldName: string;
34
+ readonly fieldName: ComponentOrFieldName;
35
35
  readonly readerAst: ReaderAst<TReadFromStore>;
36
36
  readonly resolver: (
37
37
  data: ResolverFirstParameter<TReadFromStore>,
@@ -110,7 +110,7 @@ export type ReaderLinkedField = {
110
110
  readonly arguments: Arguments | null;
111
111
  readonly condition: EagerReaderArtifact<
112
112
  { data: any; parameters: any; startUpdate?: StartUpdate<any> },
113
- StoreLink | null
113
+ StoreLink | null | (StoreLink | null)[] | StoreLink[]
114
114
  > | null;
115
115
  readonly isUpdatable: boolean;
116
116
  /**
@@ -119,6 +119,10 @@ export type ReaderLinkedField = {
119
119
  readonly refetchQueryIndex: number | null;
120
120
  };
121
121
 
122
+ export interface ReaderClientPointer extends ReaderLinkedField {
123
+ readonly refetchQueryIndex: number;
124
+ }
125
+
122
126
  export type ReaderNonLoadableResolverField = {
123
127
  readonly kind: 'Resolver';
124
128
  readonly alias: string;
@@ -147,10 +151,10 @@ export type LoadablySelectedField = {
147
151
  readonly queryArguments: Arguments | null;
148
152
  readonly refetchReaderAst: ReaderAst<any>;
149
153
 
150
- // TODO we should not type these as any
154
+ // TODO we should not type these as any.
151
155
  readonly entrypoint:
152
- | IsographEntrypoint<any, any, any>
153
- | IsographEntrypointLoader<any, any>;
156
+ | IsographEntrypoint<any, any, any, any>
157
+ | IsographEntrypointLoader<any, any, any>;
154
158
  };
155
159
 
156
160
  export type StableId = string;
@@ -173,5 +177,5 @@ export type LoadableField<
173
177
  // user-facing API. Users should only interact with LoadableFields via APIs
174
178
  // like useClientSideDefer. These APIs should have a nullable fetchOptions
175
179
  // parameter, and provide a default value ({}) to the LoadableField.
176
- fetchOptions: FetchOptions<TResult>,
180
+ fetchOptions: FetchOptions<TResult, never>,
177
181
  ) => [StableId, Factory<FragmentReference<TReadFromStore, TResult>>];