@isograph/react 0.0.0-main-3f26c9c8 → 0.0.0-main-84b67b62

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 (41) hide show
  1. package/.turbo/turbo-compile-libs.log +1 -1
  2. package/dist/core/IsographEnvironment.d.ts +9 -6
  3. package/dist/core/IsographEnvironment.d.ts.map +1 -1
  4. package/dist/core/IsographEnvironment.js +7 -1
  5. package/dist/core/cache.d.ts +2 -1
  6. package/dist/core/cache.d.ts.map +1 -1
  7. package/dist/core/cache.js +17 -27
  8. package/dist/core/check.d.ts.map +1 -1
  9. package/dist/core/check.js +10 -7
  10. package/dist/core/garbageCollection.d.ts +2 -1
  11. package/dist/core/garbageCollection.d.ts.map +1 -1
  12. package/dist/core/garbageCollection.js +21 -13
  13. package/dist/core/logging.d.ts +3 -2
  14. package/dist/core/logging.d.ts.map +1 -1
  15. package/dist/core/makeNetworkRequest.d.ts.map +1 -1
  16. package/dist/core/makeNetworkRequest.js +10 -1
  17. package/dist/core/optimisticProxy.d.ts +51 -0
  18. package/dist/core/optimisticProxy.d.ts.map +1 -0
  19. package/dist/core/optimisticProxy.js +372 -0
  20. package/dist/core/read.d.ts.map +1 -1
  21. package/dist/core/read.js +5 -4
  22. package/dist/core/startUpdate.d.ts +2 -1
  23. package/dist/core/startUpdate.d.ts.map +1 -1
  24. package/dist/core/startUpdate.js +31 -32
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/package.json +4 -4
  28. package/src/core/IsographEnvironment.ts +16 -6
  29. package/src/core/cache.ts +21 -14
  30. package/src/core/check.ts +11 -6
  31. package/src/core/garbageCollection.ts +27 -15
  32. package/src/core/logging.ts +2 -2
  33. package/src/core/makeNetworkRequest.ts +14 -1
  34. package/src/core/optimisticProxy.ts +510 -0
  35. package/src/core/read.ts +2 -1
  36. package/src/core/startUpdate.ts +44 -28
  37. package/src/index.ts +1 -1
  38. package/src/tests/garbageCollection.test.ts +2 -2
  39. package/src/tests/normalizeData.test.ts +5 -3
  40. package/src/tests/optimisticProxy.test.ts +860 -0
  41. package/src/tests/startUpdate.test.ts +7 -5
@@ -1,4 +1,5 @@
1
1
  import { ParentCache } from '@isograph/react-disposable-state';
2
+ import type { Brand } from './brand';
2
3
  import {
3
4
  IsographEntrypoint,
4
5
  IsographOperation,
@@ -12,10 +13,10 @@ import {
12
13
  } from './FragmentReference';
13
14
  import type { RetainedQuery } from './garbageCollection';
14
15
  import { LogFunction, WrappedLogFunction } from './logging';
16
+ import { type StoreLayer } from './optimisticProxy';
15
17
  import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
16
18
  import { WithEncounteredRecords } from './read';
17
19
  import type { ReaderAst, StartUpdate } from './reader';
18
- import type { Brand } from './brand';
19
20
 
20
21
  export type ComponentOrFieldName = string;
21
22
  export type StringifiedArgs = string;
@@ -56,7 +57,7 @@ export type Subscriptions = Set<Subscription>;
56
57
  export type CacheMap<T> = { [index: string]: ParentCache<T> };
57
58
 
58
59
  export type IsographEnvironment = {
59
- readonly store: IsographStore;
60
+ store: StoreLayer;
60
61
  readonly networkFunction: IsographNetworkFunction;
61
62
  readonly missingFieldHandler: MissingFieldHandler | null;
62
63
  readonly componentCache: FieldCache<React.FC<any>>;
@@ -125,18 +126,21 @@ export type DataId = string;
125
126
 
126
127
  export const ROOT_ID: DataId & '__ROOT' = '__ROOT';
127
128
 
128
- export type IsographStore = {
129
+ export type StoreLayerData = {
129
130
  [index: TypeName]: {
130
131
  [index: DataId]: StoreRecord | null;
131
132
  } | null;
133
+ };
134
+
135
+ export interface BaseStoreLayerData extends StoreLayerData {
132
136
  readonly Query: {
133
137
  readonly __ROOT: StoreRecord;
134
138
  };
135
- };
139
+ }
136
140
 
137
141
  const DEFAULT_GC_BUFFER_SIZE = 10;
138
142
  export function createIsographEnvironment(
139
- store: IsographStore,
143
+ baseStoreLayerData: BaseStoreLayerData,
140
144
  networkFunction: IsographNetworkFunction,
141
145
  missingFieldHandler?: MissingFieldHandler | null,
142
146
  logFunction?: LogFunction | null,
@@ -144,6 +148,12 @@ export function createIsographEnvironment(
144
148
  logFunction?.({
145
149
  kind: 'EnvironmentCreated',
146
150
  });
151
+ let store = {
152
+ kind: 'BaseStoreLayer',
153
+ data: baseStoreLayerData,
154
+ parentStoreLayer: null,
155
+ childStoreLayer: null,
156
+ } as const;
147
157
  return {
148
158
  store,
149
159
  networkFunction,
@@ -160,7 +170,7 @@ export function createIsographEnvironment(
160
170
  };
161
171
  }
162
172
 
163
- export function createIsographStore(): IsographStore {
173
+ export function createIsographStore(): BaseStoreLayerData {
164
174
  return {
165
175
  Query: {
166
176
  [ROOT_ID]: {},
package/src/core/cache.ts CHANGED
@@ -34,6 +34,10 @@ import {
34
34
  } from './IsographEnvironment';
35
35
  import { logMessage } from './logging';
36
36
  import { maybeMakeNetworkRequest } from './makeNetworkRequest';
37
+ import {
38
+ getMutableStoreRecordProxy,
39
+ type StoreLayerWithData,
40
+ } from './optimisticProxy';
37
41
  import { wrapPromise, wrapResolvedValue } from './PromiseWrapper';
38
42
  import { readButDoNotEvaluate, WithEncounteredRecords } from './read';
39
43
  import { ReaderLinkedField, ReaderScalarField, type ReaderAst } from './reader';
@@ -154,13 +158,13 @@ export type NetworkResponseObject = {
154
158
 
155
159
  export function normalizeData(
156
160
  environment: IsographEnvironment,
161
+ storeLayer: StoreLayerWithData,
157
162
  normalizationAst: NormalizationAstNodes,
158
163
  networkResponse: NetworkResponseObject,
159
164
  variables: Variables,
160
165
  root: StoreLink,
166
+ encounteredIds: EncounteredIds,
161
167
  ): EncounteredIds {
162
- const encounteredIds: EncounteredIds = new Map();
163
-
164
168
  logMessage(environment, () => ({
165
169
  kind: 'AboutToNormalize',
166
170
  normalizationAst,
@@ -168,11 +172,11 @@ export function normalizeData(
168
172
  variables,
169
173
  }));
170
174
 
171
- const recordsById = (environment.store[root.__typename] ??= {});
172
- const newStoreRecord = (recordsById[root.__link] ??= {});
175
+ const newStoreRecord = getMutableStoreRecordProxy(storeLayer, root);
173
176
 
174
177
  normalizeDataIntoRecord(
175
178
  environment,
179
+ storeLayer,
176
180
  normalizationAst,
177
181
  networkResponse,
178
182
  newStoreRecord,
@@ -181,13 +185,6 @@ export function normalizeData(
181
185
  encounteredIds,
182
186
  );
183
187
 
184
- logMessage(environment, () => ({
185
- kind: 'AfterNormalization',
186
- store: environment.store,
187
- encounteredIds,
188
- }));
189
-
190
- callSubscriptions(environment, encounteredIds);
191
188
  return encounteredIds;
192
189
  }
193
190
 
@@ -421,6 +418,7 @@ export type EncounteredIds = Map<TypeName, Set<DataId>>;
421
418
  */
422
419
  function normalizeDataIntoRecord(
423
420
  environment: IsographEnvironment,
421
+ storeLayer: StoreLayerWithData,
424
422
  normalizationAst: NormalizationAstNodes,
425
423
  networkResponseParentRecord: NetworkResponseObject,
426
424
  targetParentRecord: StoreRecord,
@@ -445,6 +443,7 @@ function normalizeDataIntoRecord(
445
443
  case 'Linked': {
446
444
  const linkedFieldResultedInChange = normalizeLinkedField(
447
445
  environment,
446
+ storeLayer,
448
447
  normalizationNode,
449
448
  networkResponseParentRecord,
450
449
  targetParentRecord,
@@ -459,6 +458,7 @@ function normalizeDataIntoRecord(
459
458
  case 'InlineFragment': {
460
459
  const inlineFragmentResultedInChange = normalizeInlineFragment(
461
460
  environment,
461
+ storeLayer,
462
462
  normalizationNode,
463
463
  networkResponseParentRecord,
464
464
  targetParentRecord,
@@ -526,6 +526,7 @@ function normalizeScalarField(
526
526
  */
527
527
  function normalizeLinkedField(
528
528
  environment: IsographEnvironment,
529
+ storeLayer: StoreLayerWithData,
529
530
  astNode: NormalizationLinkedField,
530
531
  networkResponseParentRecord: NetworkResponseObject,
531
532
  targetParentRecord: StoreRecord,
@@ -563,6 +564,7 @@ function normalizeLinkedField(
563
564
  }
564
565
  const newStoreRecordId = normalizeNetworkResponseObject(
565
566
  environment,
567
+ storeLayer,
566
568
  astNode,
567
569
  networkResponseObject,
568
570
  targetParentRecordLink,
@@ -589,6 +591,7 @@ function normalizeLinkedField(
589
591
  } else {
590
592
  const newStoreRecordId = normalizeNetworkResponseObject(
591
593
  environment,
594
+ storeLayer,
592
595
  astNode,
593
596
  networkResponseData,
594
597
  targetParentRecordLink,
@@ -622,6 +625,7 @@ function normalizeLinkedField(
622
625
  */
623
626
  function normalizeInlineFragment(
624
627
  environment: IsographEnvironment,
628
+ storeLayer: StoreLayerWithData,
625
629
  astNode: NormalizationInlineFragment,
626
630
  networkResponseParentRecord: NetworkResponseObject,
627
631
  targetParentRecord: StoreRecord,
@@ -633,6 +637,7 @@ function normalizeInlineFragment(
633
637
  if (networkResponseParentRecord[TYPENAME_FIELD_NAME] === typeToRefineTo) {
634
638
  const hasBeenModified = normalizeDataIntoRecord(
635
639
  environment,
640
+ storeLayer,
636
641
  astNode.selections,
637
642
  networkResponseParentRecord,
638
643
  targetParentRecord,
@@ -670,6 +675,7 @@ function dataIdsAreTheSame(
670
675
 
671
676
  function normalizeNetworkResponseObject(
672
677
  environment: IsographEnvironment,
678
+ storeLayer: StoreLayerWithData,
673
679
  astNode: NormalizationLinkedField,
674
680
  networkResponseData: NetworkResponseObject,
675
681
  targetParentRecordLink: StoreLink,
@@ -694,15 +700,16 @@ function normalizeNetworkResponseObject(
694
700
  );
695
701
  }
696
702
 
697
- const recordsById = (environment.store[__typename] ??= {});
698
- const newStoreRecord = (recordsById[newStoreRecordId] ??= {});
703
+ const link = { __link: newStoreRecordId, __typename };
704
+ const newStoreRecord = getMutableStoreRecordProxy(storeLayer, link);
699
705
 
700
706
  normalizeDataIntoRecord(
701
707
  environment,
708
+ storeLayer,
702
709
  astNode.selections,
703
710
  networkResponseData,
704
711
  newStoreRecord,
705
- { __link: newStoreRecordId, __typename: __typename },
712
+ link,
706
713
  variables,
707
714
  mutableEncounteredIds,
708
715
  );
package/src/core/check.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  StoreRecord,
9
9
  } from './IsographEnvironment';
10
10
  import { logMessage } from './logging';
11
+ import { getStoreRecordProxy } from './optimisticProxy';
11
12
 
12
13
  export type ShouldFetch = RequiredShouldFetch | 'IfNecessary';
13
14
  export type RequiredShouldFetch = 'Yes' | 'No';
@@ -39,8 +40,14 @@ export function check(
39
40
  variables: Variables,
40
41
  root: StoreLink,
41
42
  ): CheckResult {
42
- const recordsById = (environment.store[root.__typename] ??= {});
43
- const newStoreRecord = (recordsById[root.__link] ??= {});
43
+ const newStoreRecord = getStoreRecordProxy(environment.store, root);
44
+
45
+ if (newStoreRecord == null) {
46
+ return {
47
+ kind: 'MissingData',
48
+ record: root,
49
+ };
50
+ }
44
51
 
45
52
  const checkResult = checkFromRecord(
46
53
  environment,
@@ -107,8 +114,7 @@ function checkFromRecord(
107
114
  );
108
115
  }
109
116
 
110
- const linkedRecord =
111
- environment.store[link.__typename]?.[link.__link];
117
+ const linkedRecord = getStoreRecordProxy(environment.store, link);
112
118
 
113
119
  if (linkedRecord === undefined) {
114
120
  return {
@@ -141,8 +147,7 @@ function checkFromRecord(
141
147
  );
142
148
  }
143
149
 
144
- const linkedRecord =
145
- environment.store[link.__typename]?.[link.__link];
150
+ const linkedRecord = getStoreRecordProxy(environment.store, link);
146
151
 
147
152
  if (linkedRecord === undefined) {
148
153
  return {
@@ -5,11 +5,12 @@ import {
5
5
  assertLink,
6
6
  DataId,
7
7
  IsographEnvironment,
8
- IsographStore,
9
8
  StoreRecord,
9
+ type StoreLayerData,
10
10
  type StoreLink,
11
11
  type TypeName,
12
12
  } from './IsographEnvironment';
13
+ import type { StoreLayer } from './optimisticProxy';
13
14
  import {
14
15
  NOT_SET,
15
16
  type PromiseWrapper,
@@ -62,8 +63,6 @@ export function retainQuery(
62
63
  }
63
64
 
64
65
  export function garbageCollectEnvironment(environment: IsographEnvironment) {
65
- const retainedIds: RetainedIds = {};
66
-
67
66
  const retainedQueries: RetainedQueryWithNormalizationAst[] = [];
68
67
  for (const query of environment.retainedQueries) {
69
68
  if (!isRetainedQueryWithNormalizationAst(query)) {
@@ -79,18 +78,31 @@ export function garbageCollectEnvironment(environment: IsographEnvironment) {
79
78
  retainedQueries.push(query);
80
79
  }
81
80
 
81
+ let node: StoreLayer | null = environment.store;
82
+ while (node !== null) {
83
+ garbageCollectLayer(retainedQueries, node.data);
84
+ node = node.parentStoreLayer;
85
+ }
86
+ }
87
+
88
+ export function garbageCollectLayer(
89
+ retainedQueries: RetainedQueryWithNormalizationAst[],
90
+ dataLayer: StoreLayerData,
91
+ ) {
92
+ const retainedIds: RetainedIds = {};
93
+
82
94
  for (const query of retainedQueries) {
83
- recordReachableIds(environment.store, query, retainedIds);
95
+ recordReachableIds(dataLayer, query, retainedIds);
84
96
  }
85
97
 
86
- for (const typeName in environment.store) {
87
- const dataById = environment.store[typeName];
98
+ for (const typeName in dataLayer) {
99
+ const dataById = dataLayer[typeName];
88
100
  if (dataById == null) continue;
89
101
  const retainedTypeIds = retainedIds[typeName];
90
102
 
91
103
  // delete all objects
92
104
  if (retainedTypeIds == undefined || retainedTypeIds.size == 0) {
93
- delete environment.store[typeName];
105
+ delete dataLayer[typeName];
94
106
  continue;
95
107
  }
96
108
 
@@ -101,7 +113,7 @@ export function garbageCollectEnvironment(environment: IsographEnvironment) {
101
113
  }
102
114
 
103
115
  if (Object.keys(dataById).length === 0) {
104
- delete environment.store[typeName];
116
+ delete dataLayer[typeName];
105
117
  }
106
118
  }
107
119
  }
@@ -111,12 +123,12 @@ interface RetainedIds {
111
123
  }
112
124
 
113
125
  function recordReachableIds(
114
- store: IsographStore,
126
+ dataLayer: StoreLayerData,
115
127
  retainedQuery: RetainedQueryWithNormalizationAst,
116
128
  mutableRetainedIds: RetainedIds,
117
129
  ) {
118
130
  const record =
119
- store[retainedQuery.root.__typename]?.[retainedQuery.root.__link];
131
+ dataLayer[retainedQuery.root.__typename]?.[retainedQuery.root.__link];
120
132
 
121
133
  const retainedRecordsIds = (mutableRetainedIds[
122
134
  retainedQuery.root.__typename
@@ -125,7 +137,7 @@ function recordReachableIds(
125
137
 
126
138
  if (record) {
127
139
  recordReachableIdsFromRecord(
128
- store,
140
+ dataLayer,
129
141
  record,
130
142
  mutableRetainedIds,
131
143
  retainedQuery.normalizationAst.result.value.selections,
@@ -135,7 +147,7 @@ function recordReachableIds(
135
147
  }
136
148
 
137
149
  function recordReachableIdsFromRecord(
138
- store: IsographStore,
150
+ dataLayer: StoreLayerData,
139
151
  currentRecord: StoreRecord,
140
152
  mutableRetainedIds: RetainedIds,
141
153
  selections: NormalizationAstNodes,
@@ -164,7 +176,7 @@ function recordReachableIdsFromRecord(
164
176
 
165
177
  let typeStore =
166
178
  selection.concreteType !== null
167
- ? store[selection.concreteType]
179
+ ? dataLayer[selection.concreteType]
168
180
  : null;
169
181
 
170
182
  if (typeStore == null && selection.concreteType !== null) {
@@ -174,7 +186,7 @@ function recordReachableIdsFromRecord(
174
186
  for (const nextRecordLink of links) {
175
187
  let __typename = nextRecordLink.__typename;
176
188
 
177
- const resolvedTypeStore = typeStore ?? store[__typename];
189
+ const resolvedTypeStore = typeStore ?? dataLayer[__typename];
178
190
 
179
191
  if (resolvedTypeStore == null) {
180
192
  continue;
@@ -186,7 +198,7 @@ function recordReachableIdsFromRecord(
186
198
  new Set());
187
199
  retainedRecordsIds.add(nextRecordLink.__link);
188
200
  recordReachableIdsFromRecord(
189
- store,
201
+ dataLayer,
190
202
  nextRecord,
191
203
  mutableRetainedIds,
192
204
  selection.selections,
@@ -9,12 +9,12 @@ import {
9
9
  import { FragmentReference, Variables } from './FragmentReference';
10
10
  import {
11
11
  IsographEnvironment,
12
- IsographStore,
13
12
  StoreRecord,
14
13
  type StoreLink,
15
14
  } from './IsographEnvironment';
16
15
  import { ReadDataResult } from './read';
17
16
  import { Arguments } from './util';
17
+ import type { StoreLayer } from './optimisticProxy';
18
18
 
19
19
  export type LogMessage =
20
20
  | {
@@ -25,7 +25,7 @@ export type LogMessage =
25
25
  }
26
26
  | {
27
27
  kind: 'AfterNormalization';
28
- store: IsographStore;
28
+ store: StoreLayer;
29
29
  encounteredIds: EncounteredIds;
30
30
  }
31
31
  | {
@@ -1,5 +1,5 @@
1
1
  import { ItemCleanupPair } from '@isograph/disposable-types';
2
- import { normalizeData } from './cache';
2
+ import { callSubscriptions, normalizeData, type EncounteredIds } from './cache';
3
3
  import { check, DEFAULT_SHOULD_FETCH_VALUE, FetchOptions } from './check';
4
4
  import { getOrCreateCachedComponent } from './componentCache';
5
5
  import {
@@ -22,6 +22,7 @@ import {
22
22
  } from './garbageCollection';
23
23
  import { IsographEnvironment, ROOT_ID, StoreLink } from './IsographEnvironment';
24
24
  import { logMessage } from './logging';
25
+ import { addNetworkResponseStoreLayer } from './optimisticProxy';
25
26
  import {
26
27
  AnyError,
27
28
  PromiseWrapper,
@@ -201,13 +202,25 @@ export function makeNetworkRequest<
201
202
 
202
203
  const root = { __link: ROOT_ID, __typename: artifact.concreteType };
203
204
  if (status.kind === 'Undisposed') {
205
+ const encounteredIds: EncounteredIds = new Map();
206
+ environment.store = addNetworkResponseStoreLayer(environment.store);
204
207
  normalizeData(
205
208
  environment,
209
+ environment.store,
206
210
  normalizationAst.selections,
207
211
  networkResponse.data ?? {},
208
212
  variables,
209
213
  root,
214
+ encounteredIds,
210
215
  );
216
+
217
+ logMessage(environment, () => ({
218
+ kind: 'AfterNormalization',
219
+ store: environment.store,
220
+ encounteredIds: encounteredIds,
221
+ }));
222
+
223
+ callSubscriptions(environment, encounteredIds);
211
224
  }
212
225
 
213
226
  const onComplete = fetchOptions?.onComplete;