@isograph/react 0.0.0-main-ae8aa2fe → 0.0.0-main-d3ef6e33

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 (42) hide show
  1. package/dist/core/FragmentReference.d.ts +2 -0
  2. package/dist/core/PromiseWrapper.d.ts +17 -4
  3. package/dist/core/PromiseWrapper.js +35 -10
  4. package/dist/core/cache.d.ts +2 -2
  5. package/dist/core/cache.js +31 -4
  6. package/dist/core/componentCache.d.ts +2 -1
  7. package/dist/core/componentCache.js +2 -2
  8. package/dist/core/entrypoint.d.ts +2 -0
  9. package/dist/core/makeNetworkRequest.d.ts +2 -2
  10. package/dist/core/makeNetworkRequest.js +6 -2
  11. package/dist/core/read.d.ts +5 -1
  12. package/dist/core/read.js +75 -33
  13. package/dist/index.d.ts +6 -6
  14. package/dist/index.js +6 -3
  15. package/dist/loadable-hooks/useClientSideDefer.js +0 -1
  16. package/dist/react/{FragmentReferenceReader.d.ts → FragmentReader.d.ts} +4 -1
  17. package/dist/react/{FragmentReferenceReader.js → FragmentReader.js} +5 -4
  18. package/dist/react/useImperativeReference.d.ts +1 -1
  19. package/dist/react/useImperativeReference.js +3 -2
  20. package/dist/react/useLazyReference.js +2 -3
  21. package/dist/react/useReadAndSubscribe.d.ts +2 -2
  22. package/dist/react/useReadAndSubscribe.js +5 -3
  23. package/dist/react/useRerenderOnChange.d.ts +1 -2
  24. package/dist/react/useRerenderOnChange.js +3 -1
  25. package/dist/react/useResult.d.ts +4 -1
  26. package/dist/react/useResult.js +17 -4
  27. package/package.json +3 -3
  28. package/src/core/FragmentReference.ts +2 -0
  29. package/src/core/PromiseWrapper.ts +58 -12
  30. package/src/core/cache.ts +80 -57
  31. package/src/core/componentCache.ts +6 -1
  32. package/src/core/entrypoint.ts +1 -0
  33. package/src/core/makeNetworkRequest.ts +11 -6
  34. package/src/core/read.ts +108 -41
  35. package/src/index.ts +35 -25
  36. package/src/loadable-hooks/useClientSideDefer.ts +1 -1
  37. package/src/react/{FragmentReferenceReader.tsx → FragmentReader.tsx} +8 -2
  38. package/src/react/useImperativeReference.ts +4 -3
  39. package/src/react/useLazyReference.ts +2 -3
  40. package/src/react/useReadAndSubscribe.ts +8 -5
  41. package/src/react/useRerenderOnChange.ts +2 -2
  42. package/src/react/useResult.ts +28 -1
@@ -1,6 +1,7 @@
1
1
  import { DataId } from './IsographEnvironment';
2
2
  import { RefetchQueryNormalizationArtifactWrapper } from '../core/entrypoint';
3
3
  import { TopLevelReaderArtifact } from './reader';
4
+ import { PromiseWrapper } from './PromiseWrapper';
4
5
  export type VariableValue = string | number | boolean | null | object;
5
6
  export type Variables = {
6
7
  readonly [index: string]: VariableValue;
@@ -11,4 +12,5 @@ export type FragmentReference<TReadFromStore extends Object, TClientFieldValue>
11
12
  readonly root: DataId;
12
13
  readonly variables: Variables | null;
13
14
  readonly nestedRefetchQueries: RefetchQueryNormalizationArtifactWrapper[];
15
+ readonly networkRequest: PromiseWrapper<void, any>;
14
16
  };
@@ -1,13 +1,26 @@
1
+ export type AnyError = any;
1
2
  declare const NOT_SET: Symbol;
2
3
  type NotSet = typeof NOT_SET;
4
+ type Result<T, E> = {
5
+ kind: 'Ok';
6
+ value: T;
7
+ } | {
8
+ kind: 'Err';
9
+ error: E;
10
+ };
3
11
  /**
4
12
  * Invariant:
5
13
  * Before the promise is resolved, value becomes non-null.
6
14
  */
7
- export type PromiseWrapper<T> = {
15
+ export type PromiseWrapper<T, E> = {
8
16
  readonly promise: Promise<T>;
9
- value: Exclude<T, NotSet> | NotSet;
17
+ result: Result<Exclude<T, NotSet>, E> | NotSet;
10
18
  };
11
- export declare function wrapPromise<T>(promise: Promise<T>): PromiseWrapper<T>;
12
- export declare function useReadPromise<T>(p: PromiseWrapper<T>): T;
19
+ export declare function wrapPromise<T>(promise: Promise<T>): PromiseWrapper<T, any>;
20
+ export declare function readPromise<T, E>(p: PromiseWrapper<T, E>): T;
21
+ export type PromiseState<T, E> = {
22
+ kind: 'Pending';
23
+ promise: Promise<T>;
24
+ } | Result<T, E>;
25
+ export declare function getPromiseState<T, E>(p: PromiseWrapper<T, E>): PromiseState<T, E>;
13
26
  export {};
@@ -1,22 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useReadPromise = exports.wrapPromise = void 0;
3
+ exports.getPromiseState = exports.readPromise = exports.wrapPromise = void 0;
4
4
  const NOT_SET = Symbol('NOT_SET');
5
5
  function wrapPromise(promise) {
6
6
  // TODO confirm suspense works if the promise is already resolved.
7
- const wrapper = { promise, value: NOT_SET };
8
- promise.then((v) => {
9
- // T is assignable to Exclude<T, Symbol> | Symbol
10
- wrapper.value = v;
7
+ const wrapper = { promise, result: NOT_SET };
8
+ promise
9
+ .then((v) => {
10
+ // v is assignable to Exclude<T, Symbol> | Symbol
11
+ wrapper.result = { kind: 'Ok', value: v };
12
+ })
13
+ .catch((e) => {
14
+ // e is assignable to Exclude<T, Symbol> | Symbol
15
+ wrapper.result = { kind: 'Err', error: e };
11
16
  });
12
17
  return wrapper;
13
18
  }
14
19
  exports.wrapPromise = wrapPromise;
15
- function useReadPromise(p) {
16
- if (p.value !== NOT_SET) {
17
- // Safety: p.value is either NOT_SET or an actual value.
18
- return p.value;
20
+ function readPromise(p) {
21
+ const { result } = p;
22
+ if (result !== NOT_SET) {
23
+ // Safety: p.result is either NOT_SET or an actual value.
24
+ const resultKind = result;
25
+ if (resultKind.kind === 'Ok') {
26
+ return resultKind.value;
27
+ }
28
+ else {
29
+ throw resultKind.error;
30
+ }
19
31
  }
20
32
  throw p.promise;
21
33
  }
22
- exports.useReadPromise = useReadPromise;
34
+ exports.readPromise = readPromise;
35
+ function getPromiseState(p) {
36
+ const { result } = p;
37
+ if (result !== NOT_SET) {
38
+ // Safety: p.result is either NOT_SET or an actual value.
39
+ const resultKind = result;
40
+ return resultKind;
41
+ }
42
+ return {
43
+ kind: 'Pending',
44
+ promise: p.promise,
45
+ };
46
+ }
47
+ exports.getPromiseState = getPromiseState;
@@ -1,5 +1,5 @@
1
1
  import { Factory, ParentCache } from '@isograph/react-disposable-state';
2
- import { PromiseWrapper } from './PromiseWrapper';
2
+ import { AnyError, PromiseWrapper } from './PromiseWrapper';
3
3
  import { DataId, type IsographEnvironment } from './IsographEnvironment';
4
4
  import { IsographEntrypoint, NormalizationAst, NormalizationLinkedField, NormalizationScalarField, RefetchQueryNormalizationArtifactWrapper } from '../core/entrypoint';
5
5
  import { ReaderLinkedField, ReaderScalarField } from './reader';
@@ -17,7 +17,7 @@ export declare function getOrCreateCache<T>(environment: IsographEnvironment, in
17
17
  * results.
18
18
  */
19
19
  export declare function stableCopy<T>(value: T): T;
20
- export declare function getOrCreateCacheForArtifact<TReadFromStore extends Object, TClientFieldValue>(environment: IsographEnvironment, artifact: IsographEntrypoint<TReadFromStore, TClientFieldValue>, variables: Variables): ParentCache<PromiseWrapper<TClientFieldValue>>;
20
+ export declare function getOrCreateCacheForArtifact<TReadFromStore extends Object, TClientFieldValue>(environment: IsographEnvironment, artifact: IsographEntrypoint<TReadFromStore, TClientFieldValue>, variables: Variables): ParentCache<PromiseWrapper<void, AnyError>>;
21
21
  type NetworkResponseScalarValue = string | number | boolean;
22
22
  type NetworkResponseValue = NetworkResponseScalarValue | null | NetworkResponseObject | NetworkResponseObject[] | NetworkResponseScalarValue[];
23
23
  type NetworkResponseObject = {
@@ -96,15 +96,41 @@ function onNextChange(environment) {
96
96
  });
97
97
  }
98
98
  exports.onNextChange = onNextChange;
99
+ // Calls to readButDoNotEvaluate can suspend (i.e. throw a promise).
100
+ // Maybe in the future, they will be able to throw errors.
101
+ //
102
+ // That's probably okay to ignore. We don't, however, want to prevent
103
+ // updating other subscriptions if one subscription had missing data.
104
+ function withErrorHandling(f) {
105
+ return (t) => {
106
+ try {
107
+ return f(t);
108
+ }
109
+ catch (_a) { }
110
+ };
111
+ }
99
112
  function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
100
- environment.subscriptions.forEach((subscription) => {
113
+ environment.subscriptions.forEach(withErrorHandling((subscription) => {
101
114
  switch (subscription.kind) {
102
115
  case 'FragmentSubscription': {
103
116
  // TODO if there are multiple components subscribed to the same
104
117
  // fragment, we will call readButNotEvaluate multiple times. We
105
118
  // should fix that.
106
119
  if (hasOverlappingIds(recordsEncounteredWhenNormalizing, subscription.encounteredDataAndRecords.encounteredRecords)) {
107
- const newEncounteredDataAndRecords = (0, read_1.readButDoNotEvaluate)(environment, subscription.fragmentReference);
120
+ const newEncounteredDataAndRecords = (0, read_1.readButDoNotEvaluate)(environment, subscription.fragmentReference,
121
+ // Is this wrong?
122
+ // Reasons to think no:
123
+ // - we are only updating the read-out value, and the network
124
+ // options only affect whether we throw.
125
+ // - the component will re-render, and re-throw on its own, anyway.
126
+ //
127
+ // Reasons to think not:
128
+ // - it seems more efficient to suspend here and not update state,
129
+ // if we expect that the component will just throw anyway
130
+ // - consistency
131
+ // - it's also weird, this is called from makeNetworkRequest, where
132
+ // we don't currently pass network request options
133
+ {});
108
134
  if (!(0, areEqualWithDeepComparison_1.areEqualObjectsWithDeepComparison)(subscription.encounteredDataAndRecords.item, newEncounteredDataAndRecords.item)) {
109
135
  if (typeof window !== 'undefined' && window.__LOG) {
110
136
  console.log('Deep equality - No', {
@@ -137,7 +163,7 @@ function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
137
163
  throw new Error('Unexpected case');
138
164
  }
139
165
  }
140
- });
166
+ }));
141
167
  }
142
168
  function hasOverlappingIds(set1, set2) {
143
169
  for (const id of set1) {
@@ -309,12 +335,13 @@ function getParentRecordKey(astNode, variables) {
309
335
  }
310
336
  exports.getParentRecordKey = getParentRecordKey;
311
337
  function getStoreKeyChunkForArgumentValue(argumentValue, variables) {
338
+ var _a;
312
339
  switch (argumentValue.kind) {
313
340
  case 'Literal': {
314
341
  return argumentValue.value;
315
342
  }
316
343
  case 'Variable': {
317
- return variables[argumentValue.name];
344
+ return (_a = variables[argumentValue.name]) !== null && _a !== void 0 ? _a : 'null';
318
345
  }
319
346
  case 'String': {
320
347
  return argumentValue.value;
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
2
  import { IsographEnvironment } from './IsographEnvironment';
3
3
  import { FragmentReference } from './FragmentReference';
4
- export declare function getOrCreateCachedComponent(environment: IsographEnvironment, componentName: string, fragmentReference: FragmentReference<any, any>): React.FC<any>;
4
+ import { NetworkRequestReaderOptions } from './read';
5
+ export declare function getOrCreateCachedComponent(environment: IsographEnvironment, componentName: string, fragmentReference: FragmentReference<any, any>, networkRequestOptions: NetworkRequestReaderOptions): React.FC<any>;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getOrCreateCachedComponent = void 0;
4
4
  const cache_1 = require("./cache");
5
5
  const useReadAndSubscribe_1 = require("../react/useReadAndSubscribe");
6
- function getOrCreateCachedComponent(environment, componentName, fragmentReference) {
6
+ function getOrCreateCachedComponent(environment, componentName, fragmentReference, networkRequestOptions) {
7
7
  var _a, _b, _c;
8
8
  // cachedComponentsById is a three layer cache: id, then component name, then
9
9
  // stringified args. These three, together, uniquely identify a read at a given
@@ -18,7 +18,7 @@ function getOrCreateCachedComponent(environment, componentName, fragmentReferenc
18
18
  byArgs[stringifiedArgs] =
19
19
  (_c = byArgs[stringifiedArgs]) !== null && _c !== void 0 ? _c : (() => {
20
20
  function Component(additionalRuntimeProps) {
21
- const data = (0, useReadAndSubscribe_1.useReadAndSubscribe)(environment, fragmentReference);
21
+ const data = (0, useReadAndSubscribe_1.useReadAndSubscribe)(fragmentReference, networkRequestOptions);
22
22
  if (typeof window !== 'undefined' && window.__LOG) {
23
23
  console.log('Component re-rendered: ' +
24
24
  componentName +
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { TopLevelReaderArtifact } from './reader';
2
3
  import { Arguments } from './util';
3
4
  export type IsographEntrypoint<TReadFromStore extends Object, TClientFieldValue> = {
@@ -37,3 +38,4 @@ export type RefetchQueryNormalizationArtifactWrapper = {
37
38
  export declare function assertIsEntrypoint<TReadFromStore extends Object, TClientFieldValue>(value: IsographEntrypoint<TReadFromStore, TClientFieldValue> | ((_: any) => any) | any): asserts value is IsographEntrypoint<TReadFromStore, TClientFieldValue>;
38
39
  export type ExtractReadFromStore<Type> = Type extends IsographEntrypoint<infer X, any> ? X : never;
39
40
  export type ExtractResolverResult<Type> = Type extends IsographEntrypoint<any, infer X> ? X : never;
41
+ export type ExtractProps<Type> = Type extends React.FC<infer X> ? X : never;
@@ -2,5 +2,5 @@ import { ItemCleanupPair } from '@isograph/disposable-types';
2
2
  import { IsographEntrypoint, RefetchQueryNormalizationArtifact } from './entrypoint';
3
3
  import { Variables } from './FragmentReference';
4
4
  import { IsographEnvironment } from './IsographEnvironment';
5
- import { PromiseWrapper } from './PromiseWrapper';
6
- export declare function makeNetworkRequest<T>(environment: IsographEnvironment, artifact: RefetchQueryNormalizationArtifact | IsographEntrypoint<any, any>, variables: Variables): ItemCleanupPair<PromiseWrapper<T>>;
5
+ import { AnyError, PromiseWrapper } from './PromiseWrapper';
6
+ export declare function makeNetworkRequest(environment: IsographEnvironment, artifact: RefetchQueryNormalizationArtifact | IsographEntrypoint<any, any>, variables: Variables): ItemCleanupPair<PromiseWrapper<void, AnyError>>;
@@ -19,6 +19,12 @@ function makeNetworkRequest(environment, artifact, variables) {
19
19
  if (typeof window !== 'undefined' && window.__LOG) {
20
20
  console.log('network response', artifact, networkResponse);
21
21
  }
22
+ if (networkResponse.errors != null) {
23
+ // @ts-expect-error Why are we getting the wrong constructor here?
24
+ throw new Error('GraphQL network response had errors', {
25
+ cause: networkResponse,
26
+ });
27
+ }
22
28
  if (status.kind === 'UndisposedIncomplete') {
23
29
  (0, cache_1.normalizeData)(environment, artifact.normalizationAst, (_a = networkResponse.data) !== null && _a !== void 0 ? _a : {}, variables, artifact.kind === 'Entrypoint' ? artifact.nestedRefetchQueries : []);
24
30
  const retainedQuery = {
@@ -31,8 +37,6 @@ function makeNetworkRequest(environment, artifact, variables) {
31
37
  };
32
38
  (0, garbageCollection_1.retainQuery)(environment, retainedQuery);
33
39
  }
34
- // TODO return null
35
- return networkResponse;
36
40
  });
37
41
  const wrapper = (0, PromiseWrapper_1.wrapPromise)(promise);
38
42
  const response = [
@@ -4,4 +4,8 @@ export type WithEncounteredRecords<T> = {
4
4
  readonly encounteredRecords: Set<DataId>;
5
5
  readonly item: T;
6
6
  };
7
- export declare function readButDoNotEvaluate<TReadFromStore extends Object>(environment: IsographEnvironment, reference: FragmentReference<TReadFromStore, unknown>): WithEncounteredRecords<TReadFromStore>;
7
+ export declare function readButDoNotEvaluate<TReadFromStore extends Object>(environment: IsographEnvironment, fragmentReference: FragmentReference<TReadFromStore, unknown>, networkRequestOptions: NetworkRequestReaderOptions): WithEncounteredRecords<TReadFromStore>;
8
+ export type NetworkRequestReaderOptions = {
9
+ suspendIfInFlight?: boolean;
10
+ throwOnNetworkError?: boolean;
11
+ };
package/dist/core/read.js CHANGED
@@ -5,14 +5,31 @@ const cache_1 = require("./cache");
5
5
  const componentCache_1 = require("./componentCache");
6
6
  const IsographEnvironment_1 = require("./IsographEnvironment");
7
7
  const makeNetworkRequest_1 = require("./makeNetworkRequest");
8
- function readButDoNotEvaluate(environment, reference) {
8
+ function readButDoNotEvaluate(environment, fragmentReference, networkRequestOptions) {
9
9
  var _a;
10
10
  const mutableEncounteredRecords = new Set();
11
- const response = readData(environment, reference.readerArtifact.readerAst, reference.root, (_a = reference.variables) !== null && _a !== void 0 ? _a : {}, reference.nestedRefetchQueries, mutableEncounteredRecords);
11
+ const response = readData(environment, fragmentReference.readerArtifact.readerAst, fragmentReference.root, (_a = fragmentReference.variables) !== null && _a !== void 0 ? _a : {}, fragmentReference.nestedRefetchQueries, fragmentReference.networkRequest, networkRequestOptions, mutableEncounteredRecords);
12
12
  if (typeof window !== 'undefined' && window.__LOG) {
13
13
  console.log('done reading', { response });
14
14
  }
15
15
  if (response.kind === 'MissingData') {
16
+ // There are two cases here that we care about:
17
+ // 1. the network request is in flight, we haven't suspend on it, and we want
18
+ // to throw if it errors out. So, networkRequestOptions.suspendIfInFlight === false
19
+ // and networkRequestOptions.throwOnNetworkError === true.
20
+ // 2. everything else
21
+ //
22
+ // In the first case, we cannot simply throw onNextChange, because if the network
23
+ // response errors out, we will not update the store, so the onNextChange promise
24
+ // will not resolve.
25
+ if (!networkRequestOptions.suspendIfInFlight &&
26
+ networkRequestOptions.throwOnNetworkError) {
27
+ // TODO assert that the network request state is not Err
28
+ throw new Promise((resolve, reject) => {
29
+ (0, cache_1.onNextChange)(environment).then(resolve);
30
+ fragmentReference.networkRequest.promise.catch(reject);
31
+ });
32
+ }
16
33
  throw (0, cache_1.onNextChange)(environment);
17
34
  }
18
35
  else {
@@ -23,7 +40,7 @@ function readButDoNotEvaluate(environment, reference) {
23
40
  }
24
41
  }
25
42
  exports.readButDoNotEvaluate = readButDoNotEvaluate;
26
- function readData(environment, ast, root, variables, nestedRefetchQueries, mutableEncounteredRecords) {
43
+ function readData(environment, ast, root, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords) {
27
44
  var _a, _b, _c, _d, _e;
28
45
  mutableEncounteredRecords.add(root);
29
46
  let storeRecord = environment.store[root];
@@ -79,7 +96,7 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, mutab
79
96
  results.push(null);
80
97
  continue;
81
98
  }
82
- const result = readData(environment, field.selections, link.__link, variables, nestedRefetchQueries, mutableEncounteredRecords);
99
+ const result = readData(environment, field.selections, link.__link, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
83
100
  if (result.kind === 'MissingData') {
84
101
  return {
85
102
  kind: 'MissingData',
@@ -122,7 +139,7 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, mutab
122
139
  break;
123
140
  }
124
141
  const targetId = link.__link;
125
- const data = readData(environment, field.selections, targetId, variables, nestedRefetchQueries, mutableEncounteredRecords);
142
+ const data = readData(environment, field.selections, targetId, variables, nestedRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
126
143
  if (data.kind === 'MissingData') {
127
144
  return {
128
145
  kind: 'MissingData',
@@ -138,7 +155,10 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, mutab
138
155
  // id field).
139
156
  const data = readData(environment, field.refetchReaderArtifact.readerAst, root, variables,
140
157
  // Refetch fields just read the id, and don't need refetch query artifacts
141
- [], mutableEncounteredRecords);
158
+ [],
159
+ // This is probably indicative of the fact that we are doing redundant checks
160
+ // on the status of this network request...
161
+ networkRequest, networkRequestOptions, mutableEncounteredRecords);
142
162
  if (data.kind === 'MissingData') {
143
163
  return {
144
164
  kind: 'MissingData',
@@ -172,7 +192,7 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, mutab
172
192
  const resolverRefetchQueries = usedRefetchQueries.map((index) => nestedRefetchQueries[index]);
173
193
  const kind = field.readerArtifact.kind;
174
194
  if (kind === 'EagerReaderArtifact') {
175
- const data = readData(environment, field.readerArtifact.readerAst, root, variables, resolverRefetchQueries, mutableEncounteredRecords);
195
+ const data = readData(environment, field.readerArtifact.readerAst, root, variables, resolverRefetchQueries, networkRequest, networkRequestOptions, mutableEncounteredRecords);
176
196
  if (data.kind === 'MissingData') {
177
197
  return {
178
198
  kind: 'MissingData',
@@ -190,15 +210,16 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, mutab
190
210
  readerArtifact: field.readerArtifact,
191
211
  root,
192
212
  variables: generateChildVariableMap(variables, field.arguments),
213
+ networkRequest,
193
214
  nestedRefetchQueries: resolverRefetchQueries,
194
- });
215
+ }, networkRequestOptions);
195
216
  }
196
217
  break;
197
218
  }
198
219
  case 'LoadablySelectedField': {
199
220
  const refetchReaderParams = readData(environment, field.refetchReaderAst, root, variables,
200
221
  // Refetch fields just read the id, and don't need refetch query artifacts
201
- [], mutableEncounteredRecords);
222
+ [], networkRequest, networkRequestOptions, mutableEncounteredRecords);
202
223
  if (refetchReaderParams.kind === 'MissingData') {
203
224
  return {
204
225
  kind: 'MissingData',
@@ -207,30 +228,37 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, mutab
207
228
  };
208
229
  }
209
230
  else {
210
- target[field.alias] = (args) => [
211
- // Stable id
212
- root + '__' + field.name,
213
- // Fetcher
214
- () => {
215
- // TODO we should use the reader AST for this
216
- const includeReadOutData = (variables, readOutData) => {
217
- variables.id = readOutData.id;
218
- return variables;
219
- };
220
- const localVariables = includeReadOutData(args !== null && args !== void 0 ? args : {}, refetchReaderParams.data);
221
- writeQueryArgsToVariables(localVariables, field.queryArguments, variables);
222
- const [_networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, field.entrypoint, localVariables);
223
- const fragmentReference = {
224
- kind: 'FragmentReference',
225
- readerArtifact: field.entrypoint.readerArtifact,
226
- // TODO localVariables is not guaranteed to have an id field
227
- root: localVariables.id,
228
- variables: localVariables,
229
- nestedRefetchQueries: field.entrypoint.nestedRefetchQueries,
230
- };
231
- return [fragmentReference, disposeNetworkRequest];
232
- },
233
- ];
231
+ target[field.alias] = (args) => {
232
+ // TODO we should use the reader AST for this
233
+ const includeReadOutData = (variables, readOutData) => {
234
+ variables.id = readOutData.id;
235
+ return variables;
236
+ };
237
+ const localVariables = includeReadOutData(args !== null && args !== void 0 ? args : {}, refetchReaderParams.data);
238
+ writeQueryArgsToVariables(localVariables, field.queryArguments, variables);
239
+ return [
240
+ // Stable id
241
+ root +
242
+ '/' +
243
+ field.name +
244
+ '/' +
245
+ stableStringifyArgs(localVariables),
246
+ // Fetcher
247
+ () => {
248
+ const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, field.entrypoint, localVariables);
249
+ const fragmentReference = {
250
+ kind: 'FragmentReference',
251
+ readerArtifact: field.entrypoint.readerArtifact,
252
+ // TODO localVariables is not guaranteed to have an id field
253
+ root: localVariables.id,
254
+ variables: localVariables,
255
+ nestedRefetchQueries: field.entrypoint.nestedRefetchQueries,
256
+ networkRequest,
257
+ };
258
+ return [fragmentReference, disposeNetworkRequest];
259
+ },
260
+ ];
261
+ };
234
262
  }
235
263
  break;
236
264
  }
@@ -301,3 +329,17 @@ function writeQueryArgsToVariables(targetVariables, queryArgs, variables) {
301
329
  }
302
330
  }
303
331
  }
332
+ // TODO use a description of the params for this?
333
+ // TODO call stableStringifyArgs on the variable values, as well.
334
+ // This doesn't matter for now, since we are just using primitive values
335
+ // in the demo.
336
+ function stableStringifyArgs(args) {
337
+ const keys = Object.keys(args);
338
+ keys.sort();
339
+ let s = '';
340
+ for (const key of keys) {
341
+ // @ts-expect-error
342
+ s += `${key}=${JSON.stringify(args[key])};`;
343
+ }
344
+ return s;
345
+ }
package/dist/index.d.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  export { retainQuery, unretainQuery, type RetainedQuery, garbageCollectEnvironment, } from './core/garbageCollection';
2
- export { type PromiseWrapper } from './core/PromiseWrapper';
2
+ export { type PromiseWrapper, readPromise, getPromiseState, } from './core/PromiseWrapper';
3
3
  export { subscribe, normalizeData } from './core/cache';
4
4
  export { makeNetworkRequest } from './core/makeNetworkRequest';
5
5
  export { ROOT_ID, type DataId, type DataTypeValue, type IsographEnvironment, type IsographNetworkFunction, type IsographStore, type Link, type StoreRecord, createIsographEnvironment, createIsographStore, defaultMissingFieldHandler, } from './core/IsographEnvironment';
6
- export { EagerReaderArtifact, ComponentReaderArtifact, RefetchReaderArtifact, ReaderAst, ReaderAstNode, ReaderLinkedField, ReaderNonLoadableResolverField, ReaderScalarField, TopLevelReaderArtifact, LoadableField, } from './core/reader';
7
- export { NormalizationAst, NormalizationAstNode, NormalizationLinkedField, NormalizationScalarField, IsographEntrypoint, assertIsEntrypoint, RefetchQueryNormalizationArtifact, RefetchQueryNormalizationArtifactWrapper, } from './core/entrypoint';
6
+ export { type EagerReaderArtifact, type ComponentReaderArtifact, type RefetchReaderArtifact, type ReaderAst, type ReaderAstNode, type ReaderLinkedField, type ReaderNonLoadableResolverField, type ReaderScalarField, type TopLevelReaderArtifact, type LoadableField, } from './core/reader';
7
+ export { type NormalizationAst, type NormalizationAstNode, type NormalizationLinkedField, type NormalizationScalarField, type IsographEntrypoint, assertIsEntrypoint, type RefetchQueryNormalizationArtifact, type RefetchQueryNormalizationArtifactWrapper, type ExtractProps, type ExtractReadFromStore, type ExtractResolverResult, } from './core/entrypoint';
8
8
  export { readButDoNotEvaluate } from './core/read';
9
- export { ExtractSecondParam, Argument, ArgumentName, ArgumentValue, Arguments, } from './core/util';
10
- export { type FragmentReference } from './core/FragmentReference';
9
+ export { type ExtractSecondParam, type Argument, type ArgumentName, type ArgumentValue, type Arguments, } from './core/util';
10
+ export { type FragmentReference, type Variables, } from './core/FragmentReference';
11
11
  export { IsographEnvironmentProvider, useIsographEnvironment, type IsographEnvironmentProviderProps, } from './react/IsographEnvironmentProvider';
12
12
  export { useImperativeReference } from './react/useImperativeReference';
13
- export { FragmentReferenceReader } from './react/FragmentReferenceReader';
13
+ export { FragmentReader } from './react/FragmentReader';
14
14
  export { useResult } from './react/useResult';
15
15
  export { useLazyReference } from './react/useLazyReference';
16
16
  export { useRerenderOnChange } from './react/useRerenderOnChange';
package/dist/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useClientSideDefer = exports.useRerenderOnChange = exports.useLazyReference = exports.useResult = exports.FragmentReferenceReader = exports.useImperativeReference = exports.useIsographEnvironment = exports.IsographEnvironmentProvider = exports.readButDoNotEvaluate = exports.assertIsEntrypoint = exports.defaultMissingFieldHandler = exports.createIsographStore = exports.createIsographEnvironment = exports.ROOT_ID = exports.makeNetworkRequest = exports.normalizeData = exports.subscribe = exports.garbageCollectEnvironment = exports.unretainQuery = exports.retainQuery = void 0;
3
+ exports.useClientSideDefer = exports.useRerenderOnChange = exports.useLazyReference = exports.useResult = exports.FragmentReader = exports.useImperativeReference = exports.useIsographEnvironment = exports.IsographEnvironmentProvider = exports.readButDoNotEvaluate = exports.assertIsEntrypoint = exports.defaultMissingFieldHandler = exports.createIsographStore = exports.createIsographEnvironment = exports.ROOT_ID = exports.makeNetworkRequest = exports.normalizeData = exports.subscribe = exports.getPromiseState = exports.readPromise = exports.garbageCollectEnvironment = exports.unretainQuery = exports.retainQuery = void 0;
4
4
  var garbageCollection_1 = require("./core/garbageCollection");
5
5
  Object.defineProperty(exports, "retainQuery", { enumerable: true, get: function () { return garbageCollection_1.retainQuery; } });
6
6
  Object.defineProperty(exports, "unretainQuery", { enumerable: true, get: function () { return garbageCollection_1.unretainQuery; } });
7
7
  Object.defineProperty(exports, "garbageCollectEnvironment", { enumerable: true, get: function () { return garbageCollection_1.garbageCollectEnvironment; } });
8
+ var PromiseWrapper_1 = require("./core/PromiseWrapper");
9
+ Object.defineProperty(exports, "readPromise", { enumerable: true, get: function () { return PromiseWrapper_1.readPromise; } });
10
+ Object.defineProperty(exports, "getPromiseState", { enumerable: true, get: function () { return PromiseWrapper_1.getPromiseState; } });
8
11
  var cache_1 = require("./core/cache");
9
12
  Object.defineProperty(exports, "subscribe", { enumerable: true, get: function () { return cache_1.subscribe; } });
10
13
  Object.defineProperty(exports, "normalizeData", { enumerable: true, get: function () { return cache_1.normalizeData; } });
@@ -24,8 +27,8 @@ Object.defineProperty(exports, "IsographEnvironmentProvider", { enumerable: true
24
27
  Object.defineProperty(exports, "useIsographEnvironment", { enumerable: true, get: function () { return IsographEnvironmentProvider_1.useIsographEnvironment; } });
25
28
  var useImperativeReference_1 = require("./react/useImperativeReference");
26
29
  Object.defineProperty(exports, "useImperativeReference", { enumerable: true, get: function () { return useImperativeReference_1.useImperativeReference; } });
27
- var FragmentReferenceReader_1 = require("./react/FragmentReferenceReader");
28
- Object.defineProperty(exports, "FragmentReferenceReader", { enumerable: true, get: function () { return FragmentReferenceReader_1.FragmentReferenceReader; } });
30
+ var FragmentReader_1 = require("./react/FragmentReader");
31
+ Object.defineProperty(exports, "FragmentReader", { enumerable: true, get: function () { return FragmentReader_1.FragmentReader; } });
29
32
  var useResult_1 = require("./react/useResult");
30
33
  Object.defineProperty(exports, "useResult", { enumerable: true, get: function () { return useResult_1.useResult; } });
31
34
  var useLazyReference_1 = require("./react/useLazyReference");
@@ -4,7 +4,6 @@ exports.useClientSideDefer = void 0;
4
4
  const IsographEnvironmentProvider_1 = require("../react/IsographEnvironmentProvider");
5
5
  const cache_1 = require("../core/cache");
6
6
  const react_disposable_state_1 = require("@isograph/react-disposable-state");
7
- // TODO allow the user to pass props somehow
8
7
  function useClientSideDefer(loadableField, args) {
9
8
  // @ts-expect-error args is missing iff it has the type void
10
9
  const [id, loader] = loadableField(args);
@@ -1,10 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { ExtractReadFromStore, IsographEntrypoint } from '../core/entrypoint';
3
3
  import { FragmentReference } from '../core/FragmentReference';
4
- export declare function FragmentReferenceReader<TProps extends Record<any, any>, TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>>>(props: TProps extends Record<string, never> ? {
4
+ import { NetworkRequestReaderOptions } from '../core/read';
5
+ export declare function FragmentReader<TProps extends Record<any, any>, TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>>>(props: TProps extends Record<string, never> ? {
5
6
  fragmentReference: FragmentReference<ExtractReadFromStore<TEntrypoint>, React.FC<{}>>;
6
7
  additionalProps?: TProps;
8
+ networkRequestOptions?: NetworkRequestReaderOptions;
7
9
  } : {
8
10
  fragmentReference: FragmentReference<ExtractReadFromStore<TEntrypoint>, React.FC<TProps>>;
9
11
  additionalProps: TProps;
12
+ networkRequestOptions?: NetworkRequestReaderOptions;
10
13
  }): React.ReactNode;
@@ -23,11 +23,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.FragmentReferenceReader = void 0;
26
+ exports.FragmentReader = void 0;
27
27
  const React = __importStar(require("react"));
28
28
  const useResult_1 = require("./useResult");
29
- function FragmentReferenceReader(props) {
30
- const Component = (0, useResult_1.useResult)(props.fragmentReference);
29
+ function FragmentReader(props) {
30
+ var _a;
31
+ const Component = (0, useResult_1.useResult)(props.fragmentReference, (_a = props.networkRequestOptions) !== null && _a !== void 0 ? _a : {});
31
32
  return React.createElement(Component, Object.assign({}, props.additionalProps));
32
33
  }
33
- exports.FragmentReferenceReader = FragmentReferenceReader;
34
+ exports.FragmentReader = FragmentReader;
@@ -3,5 +3,5 @@ import { IsographEntrypoint } from '../core/entrypoint';
3
3
  import { FragmentReference, Variables } from '../core/FragmentReference';
4
4
  export declare function useImperativeReference<TReadFromStore extends Object, TClientFieldValue>(entrypoint: IsographEntrypoint<TReadFromStore, TClientFieldValue>): {
5
5
  fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue> | UnassignedState;
6
- loadfragmentReference: (variables: Variables) => void;
6
+ loadFragmentReference: (variables: Variables) => void;
7
7
  };
@@ -10,8 +10,8 @@ function useImperativeReference(entrypoint) {
10
10
  const environment = (0, IsographEnvironmentProvider_1.useIsographEnvironment)();
11
11
  return {
12
12
  fragmentReference: state,
13
- loadfragmentReference: (variables) => {
14
- const [_networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, entrypoint, variables);
13
+ loadFragmentReference: (variables) => {
14
+ const [networkRequest, disposeNetworkRequest] = (0, makeNetworkRequest_1.makeNetworkRequest)(environment, entrypoint, variables);
15
15
  setState([
16
16
  {
17
17
  kind: 'FragmentReference',
@@ -19,6 +19,7 @@ function useImperativeReference(entrypoint) {
19
19
  root: IsographEnvironment_1.ROOT_ID,
20
20
  variables,
21
21
  nestedRefetchQueries: entrypoint.nestedRefetchQueries,
22
+ networkRequest,
22
23
  },
23
24
  () => {
24
25
  disposeNetworkRequest();
@@ -8,9 +8,7 @@ const react_disposable_state_1 = require("@isograph/react-disposable-state");
8
8
  function useLazyReference(entrypoint, variables) {
9
9
  const environment = (0, IsographEnvironmentProvider_1.useIsographEnvironment)();
10
10
  const cache = (0, cache_1.getOrCreateCacheForArtifact)(environment, entrypoint, variables);
11
- // TODO add comment explaining why we never use this value
12
- // @ts-ignore(6133)
13
- const _data = (0, react_disposable_state_1.useLazyDisposableState)(cache).state;
11
+ const networkRequest = (0, react_disposable_state_1.useLazyDisposableState)(cache).state;
14
12
  return {
15
13
  fragmentReference: {
16
14
  kind: 'FragmentReference',
@@ -18,6 +16,7 @@ function useLazyReference(entrypoint, variables) {
18
16
  root: IsographEnvironment_1.ROOT_ID,
19
17
  variables,
20
18
  nestedRefetchQueries: entrypoint.nestedRefetchQueries,
19
+ networkRequest,
21
20
  },
22
21
  };
23
22
  }
@@ -1,7 +1,7 @@
1
1
  import { FragmentReference } from '../core/FragmentReference';
2
- import { IsographEnvironment } from '../core/IsographEnvironment';
2
+ import { NetworkRequestReaderOptions } from '../core/read';
3
3
  /**
4
4
  * Read the data from a fragment reference and subscribe to updates.
5
5
  * Does not pass the data to the fragment reference's resolver function.
6
6
  */
7
- export declare function useReadAndSubscribe<TReadFromStore extends Object>(environment: IsographEnvironment, fragmentReference: FragmentReference<TReadFromStore, any>): TReadFromStore;
7
+ export declare function useReadAndSubscribe<TReadFromStore extends Object>(fragmentReference: FragmentReference<TReadFromStore, any>, networkRequestOptions: NetworkRequestReaderOptions): TReadFromStore;