@isograph/react 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-compile-libs.log +5 -0
- package/dist/core/FragmentReference.d.ts +17 -8
- package/dist/core/FragmentReference.d.ts.map +1 -1
- package/dist/core/FragmentReference.js +3 -12
- package/dist/core/IsographEnvironment.d.ts +30 -35
- package/dist/core/IsographEnvironment.d.ts.map +1 -1
- package/dist/core/IsographEnvironment.js +4 -0
- package/dist/core/PromiseWrapper.d.ts +6 -7
- package/dist/core/PromiseWrapper.d.ts.map +1 -1
- package/dist/core/PromiseWrapper.js +6 -12
- package/dist/core/areEqualWithDeepComparison.d.ts +1 -3
- package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -1
- package/dist/core/areEqualWithDeepComparison.js +16 -2
- package/dist/core/brand.d.ts +2 -0
- package/dist/core/brand.d.ts.map +1 -0
- package/dist/core/brand.js +2 -0
- package/dist/core/cache.d.ts +16 -24
- package/dist/core/cache.d.ts.map +1 -1
- package/dist/core/cache.js +105 -72
- package/dist/core/check.d.ts +11 -7
- package/dist/core/check.d.ts.map +1 -1
- package/dist/core/check.js +2 -2
- package/dist/core/componentCache.d.ts +1 -1
- package/dist/core/componentCache.d.ts.map +1 -1
- package/dist/core/componentCache.js +27 -31
- package/dist/core/entrypoint.d.ts +43 -28
- package/dist/core/entrypoint.d.ts.map +1 -1
- package/dist/core/garbageCollection.d.ts +5 -6
- package/dist/core/garbageCollection.d.ts.map +1 -1
- package/dist/core/garbageCollection.js +1 -1
- package/dist/core/logging.d.ts +23 -15
- package/dist/core/logging.d.ts.map +1 -1
- package/dist/core/logging.js +8 -5
- package/dist/core/makeNetworkRequest.d.ts +5 -5
- package/dist/core/makeNetworkRequest.d.ts.map +1 -1
- package/dist/core/makeNetworkRequest.js +113 -28
- package/dist/core/read.d.ts +16 -11
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +468 -305
- package/dist/core/reader.d.ts +33 -37
- package/dist/core/reader.d.ts.map +1 -1
- package/dist/core/startUpdate.d.ts +8 -0
- package/dist/core/startUpdate.d.ts.map +1 -0
- package/dist/core/startUpdate.js +163 -0
- package/dist/core/util.d.ts +3 -0
- package/dist/core/util.d.ts.map +1 -1
- package/dist/index.d.ts +18 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/loadable-hooks/useClientSideDefer.d.ts +4 -10
- package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -1
- package/dist/loadable-hooks/useClientSideDefer.js +2 -2
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +8 -15
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -1
- package/dist/loadable-hooks/useConnectionSpecPagination.js +6 -4
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +1 -2
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -1
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts +4 -6
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -1
- package/dist/loadable-hooks/useImperativeLoadableField.js +1 -1
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts +6 -13
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -1
- package/dist/loadable-hooks/useSkipLimitPagination.js +11 -9
- package/dist/react/FragmentReader.d.ts +7 -14
- package/dist/react/FragmentReader.d.ts.map +1 -1
- package/dist/react/FragmentReader.js +3 -30
- package/dist/react/FragmentRenderer.d.ts +15 -0
- package/dist/react/FragmentRenderer.d.ts.map +1 -0
- package/dist/react/FragmentRenderer.js +35 -0
- package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -1
- package/dist/react/LoadableFieldReader.d.ts +12 -0
- package/dist/react/LoadableFieldReader.d.ts.map +1 -0
- package/dist/react/LoadableFieldReader.js +10 -0
- package/dist/react/LoadableFieldRenderer.d.ts +13 -0
- package/dist/react/LoadableFieldRenderer.d.ts.map +1 -0
- package/dist/react/LoadableFieldRenderer.js +37 -0
- package/dist/react/useImperativeReference.d.ts +7 -10
- package/dist/react/useImperativeReference.d.ts.map +1 -1
- package/dist/react/useImperativeReference.js +8 -9
- package/dist/react/useLazyReference.d.ts +4 -7
- package/dist/react/useLazyReference.d.ts.map +1 -1
- package/dist/react/useLazyReference.js +26 -5
- package/dist/react/useReadAndSubscribe.d.ts +3 -9
- package/dist/react/useReadAndSubscribe.d.ts.map +1 -1
- package/dist/react/useReadAndSubscribe.js +7 -3
- package/dist/react/useRerenderOnChange.d.ts +1 -1
- package/dist/react/useRerenderOnChange.d.ts.map +1 -1
- package/dist/react/useResult.d.ts +3 -6
- package/dist/react/useResult.d.ts.map +1 -1
- package/dist/react/useResult.js +10 -8
- package/isograph.config.json +1 -0
- package/package.json +6 -6
- package/src/core/FragmentReference.ts +40 -16
- package/src/core/IsographEnvironment.ts +57 -39
- package/src/core/PromiseWrapper.ts +15 -18
- package/src/core/areEqualWithDeepComparison.ts +22 -2
- package/src/core/brand.ts +18 -0
- package/src/core/cache.ts +153 -113
- package/src/core/check.ts +17 -12
- package/src/core/componentCache.ts +47 -50
- package/src/core/entrypoint.ts +66 -21
- package/src/core/garbageCollection.ts +9 -9
- package/src/core/logging.ts +39 -25
- package/src/core/makeNetworkRequest.ts +212 -34
- package/src/core/read.ts +728 -440
- package/src/core/reader.ts +46 -29
- package/src/core/startUpdate.ts +334 -0
- package/src/core/util.ts +4 -0
- package/src/index.ts +89 -8
- package/src/loadable-hooks/useClientSideDefer.ts +11 -10
- package/src/loadable-hooks/useConnectionSpecPagination.ts +27 -13
- package/src/loadable-hooks/useImperativeExposedMutationField.ts +1 -1
- package/src/loadable-hooks/useImperativeLoadableField.ts +10 -12
- package/src/loadable-hooks/useSkipLimitPagination.ts +38 -19
- package/src/react/FragmentReader.tsx +23 -39
- package/src/react/FragmentRenderer.tsx +46 -0
- package/src/react/IsographEnvironmentProvider.tsx +1 -1
- package/src/react/LoadableFieldReader.tsx +40 -0
- package/src/react/LoadableFieldRenderer.tsx +41 -0
- package/src/react/useImperativeReference.ts +49 -27
- package/src/react/useLazyReference.ts +62 -14
- package/src/react/useReadAndSubscribe.ts +17 -9
- package/src/react/useRerenderOnChange.ts +2 -2
- package/src/react/useResult.ts +22 -8
- package/src/tests/__isograph/Economist/link/output_type.ts +2 -0
- package/src/tests/__isograph/Node/asEconomist/resolver_reader.ts +28 -0
- package/src/tests/__isograph/Node/link/output_type.ts +3 -0
- package/src/tests/__isograph/Query/linkedUpdate/entrypoint.ts +31 -0
- package/src/tests/__isograph/Query/linkedUpdate/normalization_ast.ts +95 -0
- package/src/tests/__isograph/Query/linkedUpdate/output_type.ts +3 -0
- package/src/tests/__isograph/Query/linkedUpdate/param_type.ts +51 -0
- package/src/tests/__isograph/Query/linkedUpdate/query_text.ts +20 -0
- package/src/tests/__isograph/Query/linkedUpdate/resolver_reader.ts +93 -0
- package/src/tests/__isograph/Query/meName/entrypoint.ts +8 -29
- package/src/tests/__isograph/Query/meName/normalization_ast.ts +25 -0
- package/src/tests/__isograph/Query/meName/query_text.ts +6 -0
- package/src/tests/__isograph/Query/meName/resolver_reader.ts +5 -0
- package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +8 -67
- package/src/tests/__isograph/Query/meNameSuccessor/normalization_ast.ts +56 -0
- package/src/tests/__isograph/Query/meNameSuccessor/query_text.ts +13 -0
- package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +10 -0
- package/src/tests/__isograph/Query/nodeField/entrypoint.ts +8 -34
- package/src/tests/__isograph/Query/nodeField/normalization_ast.ts +30 -0
- package/src/tests/__isograph/Query/nodeField/query_text.ts +6 -0
- package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +5 -0
- package/src/tests/__isograph/Query/startUpdate/entrypoint.ts +31 -0
- package/src/tests/__isograph/Query/startUpdate/normalization_ast.ts +51 -0
- package/src/tests/__isograph/Query/startUpdate/output_type.ts +3 -0
- package/src/tests/__isograph/Query/startUpdate/param_type.ts +26 -0
- package/src/tests/__isograph/Query/startUpdate/parameters_type.ts +3 -0
- package/src/tests/__isograph/Query/startUpdate/query_text.ts +11 -0
- package/src/tests/__isograph/Query/startUpdate/resolver_reader.ts +55 -0
- package/src/tests/__isograph/Query/subquery/entrypoint.ts +8 -44
- package/src/tests/__isograph/Query/subquery/normalization_ast.ts +38 -0
- package/src/tests/__isograph/Query/subquery/query_text.ts +8 -0
- package/src/tests/__isograph/Query/subquery/resolver_reader.ts +7 -0
- package/src/tests/__isograph/iso.ts +24 -3
- package/src/tests/__isograph/tsconfig.json +8 -0
- package/src/tests/garbageCollection.test.ts +10 -8
- package/src/tests/meNameSuccessor.ts +1 -1
- package/src/tests/nodeQuery.ts +2 -1
- package/src/tests/normalizeData.test.ts +1 -2
- package/src/tests/startUpdate.test.ts +205 -0
- package/tsconfig.pkg.json +1 -2
package/isograph.config.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isograph/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Use Isograph with React",
|
|
5
5
|
"homepage": "https://isograph.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
"author": "Isograph Labs",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"compile": "
|
|
11
|
+
"compile-libs": "rimraf 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": "pnpm run test && pnpm run compile",
|
|
16
|
+
"prepack": "pnpm run test && pnpm run compile-libs",
|
|
17
17
|
"tsc": "tsc",
|
|
18
|
-
"
|
|
19
|
-
"iso
|
|
18
|
+
"tsc-force": "tsc --build --clean && tsc --build --force",
|
|
19
|
+
"iso": "cross-env ISO_PRINT_ABSOLUTE_FILEPATH=1 ../../target/debug/isograph_cli --config ./isograph.config.json"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@isograph/disposable-types": "*",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@isograph/reference-counted-pointer": "*"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"react": "18.
|
|
27
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@babel/preset-typescript": "^7.24.7",
|
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
import { type Link } from './IsographEnvironment';
|
|
2
1
|
import { ReaderWithRefetchQueries } from '../core/entrypoint';
|
|
2
|
+
import { stableCopy } from './cache';
|
|
3
|
+
import { type StoreLink } 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 =
|
|
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
|
+
|
|
10
26
|
export type ExtractData<T> = T extends {
|
|
11
27
|
data: infer D extends object;
|
|
12
28
|
}
|
|
@@ -19,31 +35,39 @@ export type ExtractParameters<T> = T extends {
|
|
|
19
35
|
? P
|
|
20
36
|
: Variables;
|
|
21
37
|
|
|
38
|
+
export type ExtractStartUpdate<T extends UnknownTReadFromStore> =
|
|
39
|
+
T['startUpdate'];
|
|
40
|
+
|
|
41
|
+
export type ExtractUpdatableData<T extends UnknownTReadFromStore> =
|
|
42
|
+
ExtractUpdatableDataFromStartUpdate<ExtractStartUpdate<T>>;
|
|
43
|
+
|
|
44
|
+
export type ExtractUpdatableDataFromStartUpdate<T> =
|
|
45
|
+
T extends StartUpdate<infer D> ? D : never;
|
|
46
|
+
|
|
22
47
|
export type FragmentReference<
|
|
23
|
-
TReadFromStore extends
|
|
48
|
+
TReadFromStore extends UnknownTReadFromStore,
|
|
24
49
|
TClientFieldValue,
|
|
25
50
|
> = {
|
|
26
51
|
readonly kind: 'FragmentReference';
|
|
27
52
|
readonly readerWithRefetchQueries: PromiseWrapper<
|
|
28
53
|
ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>
|
|
29
54
|
>;
|
|
30
|
-
readonly root:
|
|
55
|
+
readonly root: StoreLink;
|
|
56
|
+
// TODO we potentially stably copy and stringify variables a lot!
|
|
57
|
+
// So, we should employ interior mutability: pretend that fragent reference
|
|
58
|
+
// is immutable, but actually store something like
|
|
59
|
+
// `Map<Variable, StablyCopiedStringifiedOutput>`
|
|
60
|
+
// and read or update that map when we would otherwise stably copy and
|
|
61
|
+
// stringify.
|
|
31
62
|
readonly variables: ExtractParameters<TReadFromStore>;
|
|
32
63
|
readonly networkRequest: PromiseWrapper<void, any>;
|
|
33
64
|
};
|
|
34
65
|
|
|
66
|
+
export type StableIdForFragmentReference = string;
|
|
67
|
+
|
|
35
68
|
export function stableIdForFragmentReference(
|
|
36
69
|
fragmentReference: FragmentReference<any, any>,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function serializeVariables(variables: Variables) {
|
|
42
|
-
let s = '';
|
|
43
|
-
const keys = Object.keys(variables);
|
|
44
|
-
keys.sort();
|
|
45
|
-
for (const key of keys) {
|
|
46
|
-
s += `${key}:${variables[key]},`;
|
|
47
|
-
}
|
|
48
|
-
return s;
|
|
70
|
+
fieldName: string,
|
|
71
|
+
): StableIdForFragmentReference {
|
|
72
|
+
return `${fragmentReference.root.__typename}/${fragmentReference.root.__link}/${fieldName}/${JSON.stringify(stableCopy(fragmentReference.variables))}`;
|
|
49
73
|
}
|
|
@@ -1,57 +1,66 @@
|
|
|
1
1
|
import { ParentCache } from '@isograph/react-disposable-state';
|
|
2
|
+
import {
|
|
3
|
+
IsographEntrypoint,
|
|
4
|
+
IsographOperation,
|
|
5
|
+
IsographPersistedOperation,
|
|
6
|
+
} from './entrypoint';
|
|
7
|
+
import {
|
|
8
|
+
FragmentReference,
|
|
9
|
+
Variables,
|
|
10
|
+
type StableIdForFragmentReference,
|
|
11
|
+
type UnknownTReadFromStore,
|
|
12
|
+
} from './FragmentReference';
|
|
2
13
|
import { RetainedQuery } from './garbageCollection';
|
|
3
|
-
import { WithEncounteredRecords } from './read';
|
|
4
|
-
import { FragmentReference, Variables } from './FragmentReference';
|
|
5
|
-
import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
|
|
6
|
-
import { IsographEntrypoint } from './entrypoint';
|
|
7
|
-
import type { ReaderAst } from './reader';
|
|
8
14
|
import { LogFunction, WrappedLogFunction } from './logging';
|
|
15
|
+
import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
|
|
16
|
+
import { WithEncounteredRecords } from './read';
|
|
17
|
+
import type { ReaderAst, StartUpdate } from './reader';
|
|
18
|
+
import type { Brand } from './brand';
|
|
9
19
|
|
|
10
20
|
export type ComponentOrFieldName = string;
|
|
11
21
|
export type StringifiedArgs = string;
|
|
12
|
-
type ComponentCache = {
|
|
13
|
-
[key: DataId]: {
|
|
14
|
-
[key: ComponentOrFieldName]: { [key: StringifiedArgs]: React.FC<any> };
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
22
|
|
|
18
|
-
export type
|
|
19
|
-
|
|
20
|
-
> = {
|
|
21
|
-
readonly kind: 'FragmentSubscription';
|
|
22
|
-
readonly callback: (
|
|
23
|
-
newEncounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
|
|
24
|
-
) => void;
|
|
25
|
-
/** The value read out from the previous call to readButDoNotEvaluate */
|
|
26
|
-
readonly encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>;
|
|
27
|
-
readonly fragmentReference: FragmentReference<TReadFromStore, any>;
|
|
28
|
-
readonly readerAst: ReaderAst<TReadFromStore>;
|
|
23
|
+
export type FieldCache<T> = {
|
|
24
|
+
[key: StableIdForFragmentReference]: T;
|
|
29
25
|
};
|
|
30
26
|
|
|
31
|
-
type
|
|
27
|
+
export type FragmentSubscription<TReadFromStore extends UnknownTReadFromStore> =
|
|
28
|
+
{
|
|
29
|
+
readonly kind: 'FragmentSubscription';
|
|
30
|
+
readonly callback: (
|
|
31
|
+
newEncounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
|
|
32
|
+
) => void;
|
|
33
|
+
/** The value read out from the previous call to readButDoNotEvaluate */
|
|
34
|
+
readonly encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>;
|
|
35
|
+
readonly fragmentReference: FragmentReference<TReadFromStore, any>;
|
|
36
|
+
readonly readerAst: ReaderAst<TReadFromStore>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type AnyChangesToRecordSubscription = {
|
|
32
40
|
readonly kind: 'AnyChangesToRecord';
|
|
33
41
|
readonly callback: () => void;
|
|
34
|
-
readonly recordLink:
|
|
42
|
+
readonly recordLink: StoreLink;
|
|
35
43
|
};
|
|
36
44
|
|
|
37
|
-
type AnyRecordSubscription = {
|
|
45
|
+
export type AnyRecordSubscription = {
|
|
38
46
|
readonly kind: 'AnyRecords';
|
|
39
47
|
readonly callback: () => void;
|
|
40
48
|
};
|
|
41
49
|
|
|
42
|
-
type Subscription =
|
|
43
|
-
| FragmentSubscription<
|
|
50
|
+
export type Subscription =
|
|
51
|
+
| FragmentSubscription<any>
|
|
44
52
|
| AnyChangesToRecordSubscription
|
|
45
53
|
| AnyRecordSubscription;
|
|
46
|
-
type Subscriptions = Set<Subscription>;
|
|
54
|
+
export type Subscriptions = Set<Subscription>;
|
|
47
55
|
// Should this be a map?
|
|
48
|
-
type CacheMap<T> = { [index: string]: ParentCache<T> };
|
|
56
|
+
export type CacheMap<T> = { [index: string]: ParentCache<T> };
|
|
49
57
|
|
|
50
58
|
export type IsographEnvironment = {
|
|
51
59
|
readonly store: IsographStore;
|
|
52
60
|
readonly networkFunction: IsographNetworkFunction;
|
|
53
61
|
readonly missingFieldHandler: MissingFieldHandler | null;
|
|
54
|
-
readonly componentCache:
|
|
62
|
+
readonly componentCache: FieldCache<React.FC<any>>;
|
|
63
|
+
readonly eagerReaderCache: FieldCache<StartUpdate<any> | undefined>;
|
|
55
64
|
readonly subscriptions: Subscriptions;
|
|
56
65
|
// N.B. this must be <any, any>, but all *usages* of this should go through
|
|
57
66
|
// a function that adds type parameters.
|
|
@@ -59,7 +68,7 @@ export type IsographEnvironment = {
|
|
|
59
68
|
// TODO make this a CacheMap and add GC
|
|
60
69
|
readonly entrypointArtifactCache: Map<
|
|
61
70
|
string,
|
|
62
|
-
PromiseWrapper<IsographEntrypoint<any, any>>
|
|
71
|
+
PromiseWrapper<IsographEntrypoint<any, any, any>>
|
|
63
72
|
>;
|
|
64
73
|
readonly retainedQueries: Set<RetainedQuery>;
|
|
65
74
|
readonly gcBuffer: Array<RetainedQuery>;
|
|
@@ -69,18 +78,23 @@ export type IsographEnvironment = {
|
|
|
69
78
|
|
|
70
79
|
export type MissingFieldHandler = (
|
|
71
80
|
storeRecord: StoreRecord,
|
|
72
|
-
root:
|
|
81
|
+
root: StoreLink,
|
|
73
82
|
fieldName: string,
|
|
74
83
|
arguments_: { [index: string]: any } | null,
|
|
75
84
|
variables: Variables | null,
|
|
76
|
-
) =>
|
|
85
|
+
) => StoreLink | undefined;
|
|
77
86
|
|
|
78
87
|
export type IsographNetworkFunction = (
|
|
79
|
-
|
|
88
|
+
operation: IsographOperation | IsographPersistedOperation,
|
|
80
89
|
variables: Variables,
|
|
81
90
|
) => Promise<any>;
|
|
82
91
|
|
|
83
|
-
export
|
|
92
|
+
export interface Link<T extends TypeName> extends StoreLink {
|
|
93
|
+
readonly __link: Brand<DataId, T>;
|
|
94
|
+
readonly __typename: T;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type StoreLink = {
|
|
84
98
|
readonly __link: DataId;
|
|
85
99
|
readonly __typename: TypeName;
|
|
86
100
|
};
|
|
@@ -95,7 +109,7 @@ export type DataTypeValue =
|
|
|
95
109
|
| string
|
|
96
110
|
| null
|
|
97
111
|
// Singular linked fields:
|
|
98
|
-
|
|
|
112
|
+
| StoreLink
|
|
99
113
|
// Plural scalar and linked fields:
|
|
100
114
|
| DataTypeValue[];
|
|
101
115
|
|
|
@@ -127,11 +141,15 @@ export function createIsographEnvironment(
|
|
|
127
141
|
missingFieldHandler?: MissingFieldHandler | null,
|
|
128
142
|
logFunction?: LogFunction | null,
|
|
129
143
|
): IsographEnvironment {
|
|
144
|
+
logFunction?.({
|
|
145
|
+
kind: 'EnvironmentCreated',
|
|
146
|
+
});
|
|
130
147
|
return {
|
|
131
148
|
store,
|
|
132
149
|
networkFunction,
|
|
133
150
|
missingFieldHandler: missingFieldHandler ?? null,
|
|
134
151
|
componentCache: {},
|
|
152
|
+
eagerReaderCache: {},
|
|
135
153
|
subscriptions: new Set(),
|
|
136
154
|
fragmentCache: {},
|
|
137
155
|
entrypointArtifactCache: new Map(),
|
|
@@ -150,7 +168,7 @@ export function createIsographStore(): IsographStore {
|
|
|
150
168
|
};
|
|
151
169
|
}
|
|
152
170
|
|
|
153
|
-
export function assertLink(link: DataTypeValue):
|
|
171
|
+
export function assertLink(link: DataTypeValue): StoreLink | null | undefined {
|
|
154
172
|
if (Array.isArray(link)) {
|
|
155
173
|
throw new Error('Unexpected array');
|
|
156
174
|
}
|
|
@@ -163,7 +181,7 @@ export function assertLink(link: DataTypeValue): Link | null | undefined {
|
|
|
163
181
|
throw new Error('Invalid link');
|
|
164
182
|
}
|
|
165
183
|
|
|
166
|
-
export function getLink(maybeLink: DataTypeValue):
|
|
184
|
+
export function getLink(maybeLink: DataTypeValue): StoreLink | null {
|
|
167
185
|
if (
|
|
168
186
|
maybeLink != null &&
|
|
169
187
|
typeof maybeLink === 'object' &&
|
|
@@ -180,8 +198,8 @@ export function getLink(maybeLink: DataTypeValue): Link | null {
|
|
|
180
198
|
export function getOrLoadIsographArtifact(
|
|
181
199
|
environment: IsographEnvironment,
|
|
182
200
|
key: string,
|
|
183
|
-
loader: () => Promise<IsographEntrypoint<any, any>>,
|
|
184
|
-
): PromiseWrapper<IsographEntrypoint<any, any>> {
|
|
201
|
+
loader: () => Promise<IsographEntrypoint<any, any, any>>,
|
|
202
|
+
): PromiseWrapper<IsographEntrypoint<any, any, any>> {
|
|
185
203
|
const value = environment.entrypointArtifactCache.get(key);
|
|
186
204
|
if (value != null) {
|
|
187
205
|
return value;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export type AnyError = any;
|
|
2
2
|
|
|
3
|
-
const NOT_SET
|
|
4
|
-
type NotSet = typeof NOT_SET;
|
|
3
|
+
export const NOT_SET = 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;
|
|
@@ -18,31 +18,32 @@ type Result<T, E> =
|
|
|
18
18
|
* Before the promise is resolved, value becomes non-null.
|
|
19
19
|
*/
|
|
20
20
|
export type PromiseWrapper<T, E = any> = {
|
|
21
|
-
readonly promise: Promise<T
|
|
21
|
+
readonly promise: Promise<Exclude<T, NotSet>>;
|
|
22
22
|
result: Result<Exclude<T, NotSet>, E> | NotSet;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
export function wrapPromise<T>(
|
|
25
|
+
export function wrapPromise<T>(
|
|
26
|
+
promise: Promise<Exclude<T, NotSet>>,
|
|
27
|
+
): PromiseWrapper<T, unknown> {
|
|
26
28
|
// TODO confirm suspense works if the promise is already resolved.
|
|
27
29
|
const wrapper: PromiseWrapper<T, any> = { promise, result: NOT_SET };
|
|
28
30
|
promise
|
|
29
31
|
.then((v) => {
|
|
30
|
-
|
|
31
|
-
wrapper.result = { kind: 'Ok', value: v as any };
|
|
32
|
+
wrapper.result = { kind: 'Ok', value: v };
|
|
32
33
|
})
|
|
33
34
|
.catch((e) => {
|
|
34
|
-
|
|
35
|
-
wrapper.result = { kind: 'Err', error: e as any };
|
|
35
|
+
wrapper.result = { kind: 'Err', error: e };
|
|
36
36
|
});
|
|
37
37
|
return wrapper;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export function wrapResolvedValue<T>(
|
|
40
|
+
export function wrapResolvedValue<T>(
|
|
41
|
+
value: Exclude<T, NotSet>,
|
|
42
|
+
): PromiseWrapper<T, never> {
|
|
41
43
|
return {
|
|
42
44
|
promise: Promise.resolve(value),
|
|
43
45
|
result: {
|
|
44
46
|
kind: 'Ok',
|
|
45
|
-
// @ts-expect-error one should not call wrapResolvedValue with NOT_SET
|
|
46
47
|
value,
|
|
47
48
|
},
|
|
48
49
|
};
|
|
@@ -51,8 +52,7 @@ export function wrapResolvedValue<T>(value: T): PromiseWrapper<T, never> {
|
|
|
51
52
|
export function readPromise<T, E>(p: PromiseWrapper<T, E>): T {
|
|
52
53
|
const { result } = p;
|
|
53
54
|
if (result !== NOT_SET) {
|
|
54
|
-
|
|
55
|
-
const resultKind = result as Result<T, any>;
|
|
55
|
+
const resultKind = result;
|
|
56
56
|
if (resultKind.kind === 'Ok') {
|
|
57
57
|
return resultKind.value;
|
|
58
58
|
} else {
|
|
@@ -73,11 +73,8 @@ export type PromiseState<T, E> =
|
|
|
73
73
|
export function getPromiseState<T, E>(
|
|
74
74
|
p: PromiseWrapper<T, E>,
|
|
75
75
|
): PromiseState<T, E> {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Safety: p.result is either NOT_SET or an actual value.
|
|
79
|
-
const resultKind = result as Result<T, any>;
|
|
80
|
-
return resultKind;
|
|
76
|
+
if (p.result !== NOT_SET) {
|
|
77
|
+
return p.result;
|
|
81
78
|
}
|
|
82
79
|
return {
|
|
83
80
|
kind: 'Pending',
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { StoreLink } from './IsographEnvironment';
|
|
1
2
|
import type { ReaderAst, ReaderLinkedField, ReaderScalarField } from './reader';
|
|
2
|
-
|
|
3
|
+
|
|
4
|
+
function mergeUsingReaderAst(
|
|
3
5
|
field: ReaderScalarField | ReaderLinkedField,
|
|
4
6
|
oldItem: unknown,
|
|
5
7
|
newItem: unknown,
|
|
@@ -38,7 +40,7 @@ export function mergeUsingReaderAst(
|
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
function mergeArraysUsingReaderAst(
|
|
42
44
|
field: ReaderScalarField | ReaderLinkedField,
|
|
43
45
|
oldItems: ReadonlyArray<unknown>,
|
|
44
46
|
newItems: Array<unknown>,
|
|
@@ -96,6 +98,24 @@ export function mergeObjectsUsingReaderAst(
|
|
|
96
98
|
}
|
|
97
99
|
break;
|
|
98
100
|
}
|
|
101
|
+
case 'Link': {
|
|
102
|
+
const key = field.alias;
|
|
103
|
+
// @ts-expect-error
|
|
104
|
+
const oldValue: StoreLink = oldItemObject[key];
|
|
105
|
+
// @ts-expect-error
|
|
106
|
+
const newValue: StoreLink = newItemObject[key];
|
|
107
|
+
|
|
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
|
+
}
|
|
99
119
|
case 'ImperativelyLoadedField':
|
|
100
120
|
case 'LoadablySelectedField':
|
|
101
121
|
break;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Suppress the TypeScript compiler warning for this branded-type trick.
|
|
2
|
+
// See discussion: https://github.com/microsoft/TypeScript/issues/202#issuecomment-436900738
|
|
3
|
+
// Pattern: “Brand<BaseType, Brand>” leverages TypeScript conditional and `infer` types to create a pseudo-nominal type,
|
|
4
|
+
// enabling BaseType to be treated as distinct only when tagged with Brand, even though Brand doesn't exist at runtime.
|
|
5
|
+
//
|
|
6
|
+
// Explanation:
|
|
7
|
+
// - Brand extends `symbol | string` acts as a “brand” identifier.
|
|
8
|
+
// - The type uses a conditional check `infer _ extends Brand ? BaseType : never` to strip out BaseType when the branding doesn't match.
|
|
9
|
+
// - This yields a branded type system: `Brand<string, "UserId">` is not accidentally assignable to `Brand<string, "ProductId">`.
|
|
10
|
+
//
|
|
11
|
+
// Usage: Helps enforce semantic distinctions (e.g., distinguishing user IDs from product IDs) even when their runtime values are both strings.
|
|
12
|
+
//
|
|
13
|
+
// Caveat: This is purely a compile-time trick—Brand is erased in emitted JavaScript, so runtime checks must rely on other mechanisms.
|
|
14
|
+
export type Brand<
|
|
15
|
+
BaseType,
|
|
16
|
+
Brand extends symbol | string,
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
> = infer _ extends Brand ? BaseType : never;
|