@isograph/react 0.2.0 → 0.3.1

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 (151) hide show
  1. package/.turbo/turbo-compile-typescript.log +4 -0
  2. package/dist/core/FragmentReference.d.ts +25 -6
  3. package/dist/core/FragmentReference.d.ts.map +1 -0
  4. package/dist/core/FragmentReference.js +3 -13
  5. package/dist/core/IsographEnvironment.d.ts +34 -26
  6. package/dist/core/IsographEnvironment.d.ts.map +1 -0
  7. package/dist/core/IsographEnvironment.js +19 -22
  8. package/dist/core/PromiseWrapper.d.ts +4 -4
  9. package/dist/core/PromiseWrapper.d.ts.map +1 -0
  10. package/dist/core/PromiseWrapper.js +9 -9
  11. package/dist/core/areEqualWithDeepComparison.d.ts +5 -3
  12. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
  13. package/dist/core/areEqualWithDeepComparison.js +89 -39
  14. package/dist/core/cache.d.ts +20 -13
  15. package/dist/core/cache.d.ts.map +1 -0
  16. package/dist/core/cache.js +205 -128
  17. package/dist/core/check.d.ts +22 -0
  18. package/dist/core/check.d.ts.map +1 -0
  19. package/dist/core/check.js +127 -0
  20. package/dist/core/componentCache.d.ts +2 -2
  21. package/dist/core/componentCache.d.ts.map +1 -0
  22. package/dist/core/componentCache.js +28 -32
  23. package/dist/core/entrypoint.d.ts +31 -15
  24. package/dist/core/entrypoint.d.ts.map +1 -0
  25. package/dist/core/entrypoint.js +1 -2
  26. package/dist/core/garbageCollection.d.ts +6 -5
  27. package/dist/core/garbageCollection.d.ts.map +1 -0
  28. package/dist/core/garbageCollection.js +49 -16
  29. package/dist/core/logging.d.ts +68 -0
  30. package/dist/core/logging.d.ts.map +1 -0
  31. package/dist/core/logging.js +22 -0
  32. package/dist/core/makeNetworkRequest.d.ts +6 -3
  33. package/dist/core/makeNetworkRequest.d.ts.map +1 -0
  34. package/dist/core/makeNetworkRequest.js +160 -19
  35. package/dist/core/read.d.ts +25 -5
  36. package/dist/core/read.d.ts.map +1 -0
  37. package/dist/core/read.js +416 -259
  38. package/dist/core/reader.d.ts +31 -15
  39. package/dist/core/reader.d.ts.map +1 -0
  40. package/dist/core/startUpdate.d.ts +5 -0
  41. package/dist/core/startUpdate.d.ts.map +1 -0
  42. package/dist/core/startUpdate.js +15 -0
  43. package/dist/core/util.d.ts +5 -0
  44. package/dist/core/util.d.ts.map +1 -0
  45. package/dist/index.d.ts +19 -14
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +11 -2
  48. package/dist/loadable-hooks/useClientSideDefer.d.ts +9 -3
  49. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
  50. package/dist/loadable-hooks/useClientSideDefer.js +6 -8
  51. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +27 -0
  52. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
  53. package/dist/loadable-hooks/useConnectionSpecPagination.js +162 -0
  54. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +2 -2
  55. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
  56. package/dist/loadable-hooks/useImperativeExposedMutationField.js +1 -2
  57. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +13 -7
  58. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
  59. package/dist/loadable-hooks/useImperativeLoadableField.js +4 -5
  60. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +13 -26
  61. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
  62. package/dist/loadable-hooks/useSkipLimitPagination.js +93 -47
  63. package/dist/react/FragmentReader.d.ts +6 -4
  64. package/dist/react/FragmentReader.d.ts.map +1 -0
  65. package/dist/react/FragmentReader.js +4 -2
  66. package/dist/react/IsographEnvironmentProvider.d.ts +1 -0
  67. package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
  68. package/dist/react/IsographEnvironmentProvider.js +3 -3
  69. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
  70. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
  71. package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
  72. package/dist/react/useImperativeReference.d.ts +8 -6
  73. package/dist/react/useImperativeReference.d.ts.map +1 -0
  74. package/dist/react/useImperativeReference.js +6 -8
  75. package/dist/react/useLazyReference.d.ts +5 -3
  76. package/dist/react/useLazyReference.d.ts.map +1 -0
  77. package/dist/react/useLazyReference.js +34 -6
  78. package/dist/react/useReadAndSubscribe.d.ts +6 -3
  79. package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
  80. package/dist/react/useReadAndSubscribe.js +13 -10
  81. package/dist/react/useRerenderOnChange.d.ts +7 -2
  82. package/dist/react/useRerenderOnChange.d.ts.map +1 -0
  83. package/dist/react/useRerenderOnChange.js +3 -4
  84. package/dist/react/useResult.d.ts +4 -3
  85. package/dist/react/useResult.d.ts.map +1 -0
  86. package/dist/react/useResult.js +14 -9
  87. package/isograph.config.json +8 -0
  88. package/package.json +14 -9
  89. package/{src/tests/schema.graphql → schema.graphql} +1 -0
  90. package/src/core/FragmentReference.ts +44 -17
  91. package/src/core/IsographEnvironment.ts +67 -50
  92. package/src/core/PromiseWrapper.ts +3 -3
  93. package/src/core/areEqualWithDeepComparison.ts +95 -41
  94. package/src/core/cache.ts +316 -169
  95. package/src/core/check.ts +212 -0
  96. package/src/core/componentCache.ts +40 -46
  97. package/src/core/entrypoint.ts +41 -16
  98. package/src/core/garbageCollection.ts +77 -26
  99. package/src/core/logging.ts +118 -0
  100. package/src/core/makeNetworkRequest.ts +249 -20
  101. package/src/core/read.ts +658 -368
  102. package/src/core/reader.ts +61 -21
  103. package/src/core/startUpdate.ts +28 -0
  104. package/src/core/util.ts +8 -0
  105. package/src/index.ts +94 -8
  106. package/src/loadable-hooks/useClientSideDefer.ts +48 -17
  107. package/src/loadable-hooks/useConnectionSpecPagination.ts +344 -0
  108. package/src/loadable-hooks/useImperativeExposedMutationField.ts +1 -1
  109. package/src/loadable-hooks/useImperativeLoadableField.ts +36 -12
  110. package/src/loadable-hooks/useSkipLimitPagination.ts +253 -94
  111. package/src/react/FragmentReader.tsx +15 -6
  112. package/src/react/IsographEnvironmentProvider.tsx +1 -1
  113. package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
  114. package/src/react/useImperativeReference.ts +50 -18
  115. package/src/react/useLazyReference.ts +79 -11
  116. package/src/react/useReadAndSubscribe.ts +33 -10
  117. package/src/react/useRerenderOnChange.ts +7 -2
  118. package/src/react/useResult.ts +30 -9
  119. package/src/tests/__isograph/Query/meName/entrypoint.ts +10 -29
  120. package/src/tests/__isograph/Query/meName/normalization_ast.ts +25 -0
  121. package/src/tests/__isograph/Query/meName/param_type.ts +5 -2
  122. package/src/tests/__isograph/Query/meName/query_text.ts +6 -0
  123. package/src/tests/__isograph/Query/meName/resolver_reader.ts +5 -0
  124. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +10 -65
  125. package/src/tests/__isograph/Query/meNameSuccessor/normalization_ast.ts +56 -0
  126. package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +9 -6
  127. package/src/tests/__isograph/Query/meNameSuccessor/query_text.ts +13 -0
  128. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +10 -0
  129. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +10 -28
  130. package/src/tests/__isograph/Query/nodeField/normalization_ast.ts +30 -0
  131. package/src/tests/__isograph/Query/nodeField/param_type.ts +7 -3
  132. package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
  133. package/src/tests/__isograph/Query/nodeField/query_text.ts +6 -0
  134. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +5 -0
  135. package/src/tests/__isograph/Query/subquery/entrypoint.ts +28 -0
  136. package/src/tests/__isograph/Query/subquery/normalization_ast.ts +38 -0
  137. package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
  138. package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
  139. package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
  140. package/src/tests/__isograph/Query/subquery/query_text.ts +8 -0
  141. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +52 -0
  142. package/src/tests/__isograph/iso.ts +24 -12
  143. package/src/tests/garbageCollection.test.ts +53 -45
  144. package/src/tests/meNameSuccessor.ts +8 -3
  145. package/src/tests/nodeQuery.ts +7 -4
  146. package/src/tests/normalizeData.test.ts +120 -0
  147. package/src/tests/tsconfig.json +3 -3
  148. package/tsconfig.json +2 -2
  149. package/tsconfig.pkg.json +7 -3
  150. package/vitest.config.ts +20 -0
  151. package/src/tests/isograph.config.json +0 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResult.d.ts","sourceRoot":"","sources":["../../src/react/useResult.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,2BAA2B,EAC5B,MAAM,cAAc,CAAC;AAKtB,wBAAgB,SAAS,CACvB,cAAc,SAAS,qBAAqB,EAC5C,iBAAiB,EAEjB,iBAAiB,EAAE,iBAAiB,CAAC,cAAc,EAAE,iBAAiB,CAAC,EACvE,4BAA4B,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,GAAG,IAAI,GACzE,iBAAiB,CA8CnB;AAED,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,EACzC,qBAAqB,EAAE,2BAA2B,QAWnD"}
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.maybeUnwrapNetworkRequest = exports.useResult = void 0;
4
- const IsographEnvironmentProvider_1 = require("../react/IsographEnvironmentProvider");
3
+ exports.useResult = useResult;
4
+ exports.maybeUnwrapNetworkRequest = maybeUnwrapNetworkRequest;
5
5
  const componentCache_1 = require("../core/componentCache");
6
- const useReadAndSubscribe_1 = require("./useReadAndSubscribe");
7
- const read_1 = require("../core/read");
8
6
  const PromiseWrapper_1 = require("../core/PromiseWrapper");
7
+ const read_1 = require("../core/read");
8
+ const startUpdate_1 = require("../core/startUpdate");
9
+ const IsographEnvironmentProvider_1 = require("../react/IsographEnvironmentProvider");
10
+ const useReadAndSubscribe_1 = require("./useReadAndSubscribe");
9
11
  function useResult(fragmentReference, partialNetworkRequestOptions) {
10
12
  const environment = (0, IsographEnvironmentProvider_1.useIsographEnvironment)();
11
13
  const networkRequestOptions = (0, read_1.getNetworkRequestOptionsWithDefaults)(partialNetworkRequestOptions);
@@ -14,15 +16,19 @@ function useResult(fragmentReference, partialNetworkRequestOptions) {
14
16
  switch (readerWithRefetchQueries.readerArtifact.kind) {
15
17
  case 'ComponentReaderArtifact': {
16
18
  // @ts-expect-error
17
- return (0, componentCache_1.getOrCreateCachedComponent)(environment, readerWithRefetchQueries.readerArtifact.componentName, fragmentReference, networkRequestOptions);
19
+ return (0, componentCache_1.getOrCreateCachedComponent)(environment, readerWithRefetchQueries.readerArtifact.fieldName, fragmentReference, networkRequestOptions);
18
20
  }
19
21
  case 'EagerReaderArtifact': {
20
- const data = (0, useReadAndSubscribe_1.useReadAndSubscribe)(fragmentReference, networkRequestOptions);
21
- return readerWithRefetchQueries.readerArtifact.resolver(data);
22
+ const data = (0, useReadAndSubscribe_1.useReadAndSubscribe)(fragmentReference, networkRequestOptions, readerWithRefetchQueries.readerArtifact.readerAst);
23
+ const param = Object.assign({ data: data, parameters: fragmentReference.variables }, (readerWithRefetchQueries.readerArtifact.hasUpdatable
24
+ ? {
25
+ startUpdate: (0, startUpdate_1.getOrCreateCachedStartUpdate)(environment, fragmentReference, readerWithRefetchQueries.readerArtifact.fieldName),
26
+ }
27
+ : undefined));
28
+ return readerWithRefetchQueries.readerArtifact.resolver(param);
22
29
  }
23
30
  }
24
31
  }
25
- exports.useResult = useResult;
26
32
  function maybeUnwrapNetworkRequest(networkRequest, networkRequestOptions) {
27
33
  const state = (0, PromiseWrapper_1.getPromiseState)(networkRequest);
28
34
  if (state.kind === 'Err' && networkRequestOptions.throwOnNetworkError) {
@@ -33,4 +39,3 @@ function maybeUnwrapNetworkRequest(networkRequest, networkRequestOptions) {
33
39
  throw state.promise;
34
40
  }
35
41
  }
36
- exports.maybeUnwrapNetworkRequest = maybeUnwrapNetworkRequest;
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "../isograph-compiler/isograph-config-schema.json",
3
+ "project_root": "./src/tests",
4
+ "schema": "./schema.graphql",
5
+ "options": {
6
+ "on_invalid_id_type": "error"
7
+ }
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isograph/react",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Use Isograph with React",
5
5
  "homepage": "https://isograph.dev",
6
6
  "main": "dist/index.js",
@@ -8,27 +8,32 @@
8
8
  "author": "Isograph Labs",
9
9
  "license": "MIT",
10
10
  "scripts": {
11
- "compile": "rm -rf dist/* && tsc -p tsconfig.pkg.json",
11
+ "compile-typescript": "rm -rf dist/* && tsc -p tsconfig.pkg.json",
12
12
  "compile-watch": "tsc -p tsconfig.pkg.json --watch",
13
13
  "test": "vitest run",
14
14
  "test-watch": "vitest watch",
15
15
  "coverage": "vitest run --coverage",
16
- "prepack": "yarn run test && yarn run compile",
17
- "tsc": "tsc"
16
+ "prepack": "pnpm run test && pnpm run compile-typescript",
17
+ "tsc": "tsc",
18
+ "tsc-force": "tsc --build --clean && tsc --build --force",
19
+ "iso": "cross-env ../../target/debug/isograph_cli --config ./isograph.config.json",
20
+ "iso-watch": "cross-env ../../target/debug/isograph_cli --config ./isograph.config.json --watch"
18
21
  },
19
22
  "dependencies": {
20
- "@isograph/disposable-types": "0.2.0",
23
+ "@isograph/disposable-types": "*",
21
24
  "@isograph/react-disposable-state": "*",
22
25
  "@isograph/reference-counted-pointer": "*"
23
26
  },
24
27
  "peerDependencies": {
25
- "react": "18.2.0"
28
+ "react": "^18.0.0 || ^19.0.0"
26
29
  },
27
30
  "devDependencies": {
28
- "@types/react": "^18.0.31",
31
+ "@babel/preset-typescript": "^7.24.7",
32
+ "@types/react": "18.3.1",
29
33
  "react-test-renderer": "^18.2.0",
30
- "vitest": "^0.29.8",
31
- "typescript": "^5.0.3"
34
+ "typescript": "5.6.3",
35
+ "vite-plugin-babel": "^1.2.0",
36
+ "vite-plugin-commonjs": "^0.10.1"
32
37
  },
33
38
  "repository": {
34
39
  "type": "git",
@@ -2,6 +2,7 @@ type Query {
2
2
  me: Economist!
3
3
  you: Economist!
4
4
  node(id: ID!): Node
5
+ query: Query!
5
6
  }
6
7
 
7
8
  type Economist implements Node {
@@ -1,37 +1,64 @@
1
- import { DataId } from './IsographEnvironment';
2
1
  import { ReaderWithRefetchQueries } from '../core/entrypoint';
2
+ import { stableCopy } from './cache';
3
+ import { type Link } from './IsographEnvironment';
3
4
  import { PromiseWrapper } from './PromiseWrapper';
5
+ import type { StartUpdate } from './reader';
4
6
 
5
7
  // TODO type this better
6
- export type VariableValue = string | number | boolean | null | object;
8
+ export type VariableValue =
9
+ | string
10
+ | number
11
+ | boolean
12
+ | null
13
+ | {
14
+ readonly [index: string]: VariableValue;
15
+ }
16
+ | VariableValue[];
7
17
 
8
18
  export type Variables = { readonly [index: string]: VariableValue };
9
19
 
20
+ export type UnknownTReadFromStore = {
21
+ parameters: object;
22
+ data: object;
23
+ startUpdate?: StartUpdate<object>;
24
+ };
25
+
26
+ export type ExtractData<T> = T extends {
27
+ data: infer D extends object;
28
+ }
29
+ ? D
30
+ : never;
31
+
32
+ export type ExtractParameters<T> = T extends {
33
+ parameters: infer P extends Variables;
34
+ }
35
+ ? P
36
+ : Variables;
37
+
38
+ export type ExtractStartUpdate<
39
+ T extends {
40
+ startUpdate?: StartUpdate<object>;
41
+ },
42
+ > = T['startUpdate'];
43
+
10
44
  export type FragmentReference<
11
- TReadFromStore extends Object,
45
+ TReadFromStore extends UnknownTReadFromStore,
12
46
  TClientFieldValue,
13
47
  > = {
14
48
  readonly kind: 'FragmentReference';
15
49
  readonly readerWithRefetchQueries: PromiseWrapper<
16
50
  ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>
17
51
  >;
18
- readonly root: DataId;
19
- readonly variables: Variables | null;
52
+ readonly root: Link;
53
+ readonly variables: ExtractParameters<TReadFromStore>;
20
54
  readonly networkRequest: PromiseWrapper<void, any>;
21
55
  };
22
56
 
57
+ export type StableIdForFragmentReference = string;
58
+
23
59
  export function stableIdForFragmentReference(
24
60
  fragmentReference: FragmentReference<any, any>,
25
- ): string {
26
- return `${fragmentReference.root}/TODO_FRAGMENT_NAME/${serializeVariables(fragmentReference.variables ?? {})}`;
27
- }
28
-
29
- function serializeVariables(variables: Variables) {
30
- let s = '';
31
- const keys = Object.keys(variables);
32
- keys.sort();
33
- for (const key of keys) {
34
- s += `${key}:${variables[key]},`;
35
- }
36
- return s;
61
+ fieldName: string,
62
+ ): StableIdForFragmentReference {
63
+ return `${fragmentReference.root.__typename}/${fragmentReference.root.__link}/${fieldName}/${JSON.stringify(stableCopy(fragmentReference.variables))}`;
37
64
  }
@@ -1,42 +1,61 @@
1
1
  import { ParentCache } from '@isograph/react-disposable-state';
2
+ import { IsographEntrypoint } from './entrypoint';
3
+ import {
4
+ FragmentReference,
5
+ Variables,
6
+ type StableIdForFragmentReference,
7
+ type UnknownTReadFromStore,
8
+ } from './FragmentReference';
2
9
  import { RetainedQuery } from './garbageCollection';
3
- import { WithEncounteredRecords } from './read';
4
- import { FragmentReference, Variables } from './FragmentReference';
10
+ import { LogFunction, WrappedLogFunction } from './logging';
5
11
  import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
6
- import { IsographEntrypoint } from './entrypoint';
12
+ import { WithEncounteredRecords } from './read';
13
+ import type { ReaderAst, StartUpdate } from './reader';
7
14
 
8
15
  export type ComponentOrFieldName = string;
9
16
  export type StringifiedArgs = string;
10
- type ComponentCache = {
11
- [key: DataId]: {
12
- [key: ComponentOrFieldName]: { [key: StringifiedArgs]: React.FC<any> };
13
- };
17
+
18
+ export type FieldCache<T> = {
19
+ [key: StableIdForFragmentReference]: T;
14
20
  };
15
21
 
16
- export type FragmentSubscription<TReadFromStore extends Object> = {
17
- readonly kind: 'FragmentSubscription';
18
- readonly callback: (
19
- newEncounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
20
- ) => void;
21
- /** The value read out from the previous call to readButDoNotEvaluate */
22
- readonly encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>;
23
- readonly fragmentReference: FragmentReference<TReadFromStore, any>;
22
+ export type FragmentSubscription<TReadFromStore extends UnknownTReadFromStore> =
23
+ {
24
+ readonly kind: 'FragmentSubscription';
25
+ readonly callback: (
26
+ newEncounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
27
+ ) => void;
28
+ /** The value read out from the previous call to readButDoNotEvaluate */
29
+ readonly encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>;
30
+ readonly fragmentReference: FragmentReference<TReadFromStore, any>;
31
+ readonly readerAst: ReaderAst<TReadFromStore>;
32
+ };
33
+
34
+ export type AnyChangesToRecordSubscription = {
35
+ readonly kind: 'AnyChangesToRecord';
36
+ readonly callback: () => void;
37
+ readonly recordLink: Link;
24
38
  };
25
- type AnyRecordSubscription = {
39
+
40
+ export type AnyRecordSubscription = {
26
41
  readonly kind: 'AnyRecords';
27
42
  readonly callback: () => void;
28
43
  };
29
44
 
30
- type Subscription = FragmentSubscription<Object> | AnyRecordSubscription;
31
- type Subscriptions = Set<Subscription>;
45
+ export type Subscription =
46
+ | FragmentSubscription<any>
47
+ | AnyChangesToRecordSubscription
48
+ | AnyRecordSubscription;
49
+ export type Subscriptions = Set<Subscription>;
32
50
  // Should this be a map?
33
- type CacheMap<T> = { [index: string]: ParentCache<T> };
51
+ export type CacheMap<T> = { [index: string]: ParentCache<T> };
34
52
 
35
53
  export type IsographEnvironment = {
36
54
  readonly store: IsographStore;
37
55
  readonly networkFunction: IsographNetworkFunction;
38
56
  readonly missingFieldHandler: MissingFieldHandler | null;
39
- readonly componentCache: ComponentCache;
57
+ readonly componentCache: FieldCache<React.FC<any>>;
58
+ readonly eagerReaderCache: FieldCache<StartUpdate<any> | undefined>;
40
59
  readonly subscriptions: Subscriptions;
41
60
  // N.B. this must be <any, any>, but all *usages* of this should go through
42
61
  // a function that adds type parameters.
@@ -44,16 +63,17 @@ export type IsographEnvironment = {
44
63
  // TODO make this a CacheMap and add GC
45
64
  readonly entrypointArtifactCache: Map<
46
65
  string,
47
- PromiseWrapper<IsographEntrypoint<any, any>>
66
+ PromiseWrapper<IsographEntrypoint<any, any, any>>
48
67
  >;
49
68
  readonly retainedQueries: Set<RetainedQuery>;
50
69
  readonly gcBuffer: Array<RetainedQuery>;
51
70
  readonly gcBufferSize: number;
71
+ readonly loggers: Set<WrappedLogFunction>;
52
72
  };
53
73
 
54
74
  export type MissingFieldHandler = (
55
75
  storeRecord: StoreRecord,
56
- root: DataId,
76
+ root: Link,
57
77
  fieldName: string,
58
78
  arguments_: { [index: string]: any } | null,
59
79
  variables: Variables | null,
@@ -66,6 +86,7 @@ export type IsographNetworkFunction = (
66
86
 
67
87
  export type Link = {
68
88
  readonly __link: DataId;
89
+ readonly __typename: TypeName;
69
90
  };
70
91
 
71
92
  export type DataTypeValue =
@@ -89,60 +110,54 @@ export type StoreRecord = {
89
110
  readonly id?: DataId;
90
111
  };
91
112
 
113
+ export type TypeName = string;
92
114
  export type DataId = string;
93
115
 
94
116
  export const ROOT_ID: DataId & '__ROOT' = '__ROOT';
95
117
 
96
118
  export type IsographStore = {
97
- [index: DataId]: StoreRecord | null;
98
- readonly __ROOT: StoreRecord;
119
+ [index: TypeName]: {
120
+ [index: DataId]: StoreRecord | null;
121
+ } | null;
122
+ readonly Query: {
123
+ readonly __ROOT: StoreRecord;
124
+ };
99
125
  };
100
126
 
101
127
  const DEFAULT_GC_BUFFER_SIZE = 10;
102
128
  export function createIsographEnvironment(
103
129
  store: IsographStore,
104
130
  networkFunction: IsographNetworkFunction,
105
- missingFieldHandler?: MissingFieldHandler,
131
+ missingFieldHandler?: MissingFieldHandler | null,
132
+ logFunction?: LogFunction | null,
106
133
  ): IsographEnvironment {
134
+ logFunction?.({
135
+ kind: 'EnvironmentCreated',
136
+ });
107
137
  return {
108
138
  store,
109
139
  networkFunction,
110
140
  missingFieldHandler: missingFieldHandler ?? null,
111
141
  componentCache: {},
142
+ eagerReaderCache: {},
112
143
  subscriptions: new Set(),
113
144
  fragmentCache: {},
114
145
  entrypointArtifactCache: new Map(),
115
146
  retainedQueries: new Set(),
116
147
  gcBuffer: [],
117
148
  gcBufferSize: DEFAULT_GC_BUFFER_SIZE,
149
+ loggers: logFunction != null ? new Set([{ log: logFunction }]) : new Set(),
118
150
  };
119
151
  }
120
152
 
121
153
  export function createIsographStore(): IsographStore {
122
154
  return {
123
- [ROOT_ID]: {},
155
+ Query: {
156
+ [ROOT_ID]: {},
157
+ },
124
158
  };
125
159
  }
126
160
 
127
- export function defaultMissingFieldHandler(
128
- _storeRecord: StoreRecord,
129
- _root: DataId,
130
- fieldName: string,
131
- arguments_: { [index: string]: any } | null,
132
- variables: Variables | null,
133
- ): Link | undefined {
134
- if (fieldName === 'node' || fieldName === 'user') {
135
- const variable = arguments_?.['id'];
136
- const value = variables?.[variable];
137
-
138
- // TODO can we handle explicit nulls here too? Probably, after wrapping in objects
139
- if (value != null) {
140
- // @ts-expect-error
141
- return { __link: value };
142
- }
143
- }
144
- }
145
-
146
161
  export function assertLink(link: DataTypeValue): Link | null | undefined {
147
162
  if (Array.isArray(link)) {
148
163
  throw new Error('Unexpected array');
@@ -160,10 +175,12 @@ export function getLink(maybeLink: DataTypeValue): Link | null {
160
175
  if (
161
176
  maybeLink != null &&
162
177
  typeof maybeLink === 'object' &&
163
- // @ts-expect-error this is safe
164
- maybeLink.__link != null
178
+ '__link' in maybeLink &&
179
+ maybeLink.__link != null &&
180
+ '__typename' in maybeLink &&
181
+ maybeLink.__typename != null
165
182
  ) {
166
- return maybeLink as any;
183
+ return maybeLink;
167
184
  }
168
185
  return null;
169
186
  }
@@ -171,8 +188,8 @@ export function getLink(maybeLink: DataTypeValue): Link | null {
171
188
  export function getOrLoadIsographArtifact(
172
189
  environment: IsographEnvironment,
173
190
  key: string,
174
- loader: () => Promise<IsographEntrypoint<any, any>>,
175
- ): PromiseWrapper<IsographEntrypoint<any, any>> {
191
+ loader: () => Promise<IsographEntrypoint<any, any, any>>,
192
+ ): PromiseWrapper<IsographEntrypoint<any, any, any>> {
176
193
  const value = environment.entrypointArtifactCache.get(key);
177
194
  if (value != null) {
178
195
  return value;
@@ -1,9 +1,9 @@
1
1
  export type AnyError = any;
2
2
 
3
- const NOT_SET: Symbol = Symbol('NOT_SET');
4
- type NotSet = typeof NOT_SET;
3
+ export const NOT_SET: Symbol = Symbol('NOT_SET');
4
+ export type NotSet = typeof NOT_SET;
5
5
 
6
- type Result<T, E> =
6
+ export type Result<T, E> =
7
7
  | {
8
8
  kind: 'Ok';
9
9
  value: T;
@@ -1,78 +1,132 @@
1
- export function areEqualWithDeepComparison(
1
+ import type { Link } from './IsographEnvironment';
2
+ import type { ReaderAst, ReaderLinkedField, ReaderScalarField } from './reader';
3
+
4
+ export function mergeUsingReaderAst(
5
+ field: ReaderScalarField | ReaderLinkedField,
2
6
  oldItem: unknown,
3
7
  newItem: unknown,
4
- ): boolean {
8
+ ): unknown {
5
9
  if (newItem === null) {
6
- return oldItem === null;
10
+ return oldItem === null ? oldItem : newItem;
7
11
  }
8
12
 
9
13
  if (newItem === undefined) {
10
- return oldItem === undefined;
14
+ return oldItem === undefined ? oldItem : newItem;
11
15
  }
12
16
 
13
17
  if (Array.isArray(newItem)) {
14
18
  if (!Array.isArray(oldItem)) {
15
- return false;
19
+ return newItem;
16
20
  }
17
21
 
18
- return areEqualArraysWithDeepComparison(oldItem, newItem);
22
+ return mergeArraysUsingReaderAst(field, oldItem, newItem);
19
23
  }
20
24
 
21
- if (typeof newItem === 'object') {
22
- if (typeof oldItem !== 'object') {
23
- return false;
24
- }
25
+ switch (field.kind) {
26
+ case 'Scalar':
27
+ return oldItem === newItem ? oldItem : newItem;
28
+ case 'Linked':
29
+ if (oldItem == null) {
30
+ return newItem;
31
+ }
25
32
 
26
- if (oldItem === null) {
27
- return false;
33
+ return mergeObjectsUsingReaderAst(field.selections, oldItem, newItem);
34
+ default: {
35
+ // Ensure we have covered all variants
36
+ let _: never = field;
37
+ _;
38
+ throw new Error('Unexpected case.');
28
39
  }
29
-
30
- return areEqualObjectsWithDeepComparison(oldItem, newItem);
31
40
  }
32
-
33
- return newItem === oldItem;
34
41
  }
35
42
 
36
- export function areEqualArraysWithDeepComparison(
43
+ export function mergeArraysUsingReaderAst(
44
+ field: ReaderScalarField | ReaderLinkedField,
37
45
  oldItems: ReadonlyArray<unknown>,
38
- newItems: ReadonlyArray<unknown>,
39
- ): boolean {
46
+ newItems: Array<unknown>,
47
+ ): ReadonlyArray<unknown> {
40
48
  if (newItems.length !== oldItems.length) {
41
- return false;
49
+ return newItems;
42
50
  }
43
51
 
52
+ let canRecycle = true;
44
53
  for (let i = 0; i < newItems.length; i++) {
45
- if (!areEqualWithDeepComparison(oldItems[i], newItems[i])) {
46
- return false;
54
+ const mergedItem = mergeUsingReaderAst(field, oldItems[i], newItems[i]);
55
+ if (mergedItem !== oldItems[i]) {
56
+ canRecycle = false;
57
+ } else {
58
+ newItems[i] = mergedItem;
47
59
  }
48
60
  }
49
61
 
50
- return true;
62
+ return canRecycle ? oldItems : newItems;
51
63
  }
52
64
 
53
- export function areEqualObjectsWithDeepComparison(
65
+ export function mergeObjectsUsingReaderAst(
66
+ ast: ReaderAst<object>,
54
67
  oldItemObject: object,
55
68
  newItemObject: object,
56
- ): boolean {
57
- const oldKeys = Object.keys(oldItemObject);
58
- const newKeys = Object.keys(newItemObject);
69
+ ): object {
70
+ let canRecycle = true;
71
+ for (const field of ast) {
72
+ switch (field.kind) {
73
+ case 'Scalar':
74
+ case 'Linked':
75
+ const key = field.alias ?? field.fieldName;
76
+ // @ts-expect-error
77
+ const oldValue = oldItemObject[key];
78
+ // @ts-expect-error
79
+ const newValue = newItemObject[key];
59
80
 
60
- if (oldKeys.length !== newKeys.length) {
61
- return false;
62
- }
81
+ const mergedValue = mergeUsingReaderAst(field, oldValue, newValue);
82
+ if (mergedValue !== oldValue) {
83
+ canRecycle = false;
84
+ } else {
85
+ // @ts-expect-error
86
+ newItemObject[key] = mergedValue;
87
+ }
88
+ break;
89
+ case 'Resolver': {
90
+ const key = field.alias;
91
+ // @ts-expect-error
92
+ const oldValue = oldItemObject[key];
93
+ // @ts-expect-error
94
+ const newValue = newItemObject[key];
63
95
 
64
- for (const oldKey of oldKeys) {
65
- if (!(oldKey in newItemObject)) {
66
- return false;
67
- }
68
- // @ts-expect-error
69
- const oldValue = oldItemObject[oldKey];
70
- // @ts-expect-error
71
- const newValue = newItemObject[oldKey];
96
+ if (oldValue !== newValue) {
97
+ canRecycle = false;
98
+ }
99
+ break;
100
+ }
101
+ case 'Link': {
102
+ const key = field.alias;
103
+ // @ts-expect-error
104
+ const oldValue: Link = oldItemObject[key];
105
+ // @ts-expect-error
106
+ const newValue: Link = newItemObject[key];
72
107
 
73
- if (!areEqualWithDeepComparison(oldValue, newValue)) {
74
- return false;
108
+ if (
109
+ oldValue.__link !== newValue.__link ||
110
+ oldValue.__typename !== newValue.__typename
111
+ ) {
112
+ canRecycle = false;
113
+ } else {
114
+ // @ts-expect-error
115
+ newItemObject[key] = oldValue;
116
+ }
117
+ break;
118
+ }
119
+ case 'ImperativelyLoadedField':
120
+ case 'LoadablySelectedField':
121
+ break;
122
+ default: {
123
+ // Ensure we have covered all variants
124
+ let _: never = field;
125
+ _;
126
+ throw new Error('Unexpected case.');
127
+ }
75
128
  }
76
129
  }
77
- return true;
130
+
131
+ return canRecycle ? oldItemObject : newItemObject;
78
132
  }