@isograph/react 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.turbo/turbo-compile-libs.log +2 -2
  2. package/dist/core/FragmentReference.d.ts +4 -2
  3. package/dist/core/FragmentReference.d.ts.map +1 -1
  4. package/dist/core/FragmentReference.js +2 -2
  5. package/dist/core/IsographEnvironment.d.ts +19 -11
  6. package/dist/core/IsographEnvironment.d.ts.map +1 -1
  7. package/dist/core/IsographEnvironment.js +27 -2
  8. package/dist/core/PromiseWrapper.d.ts +13 -7
  9. package/dist/core/PromiseWrapper.d.ts.map +1 -1
  10. package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -1
  11. package/dist/core/areEqualWithDeepComparison.js +5 -0
  12. package/dist/core/brand.d.ts +17 -0
  13. package/dist/core/brand.d.ts.map +1 -1
  14. package/dist/core/cache.d.ts +10 -7
  15. package/dist/core/cache.d.ts.map +1 -1
  16. package/dist/core/cache.js +102 -74
  17. package/dist/core/check.d.ts +8 -4
  18. package/dist/core/check.d.ts.map +1 -1
  19. package/dist/core/check.js +10 -7
  20. package/dist/core/componentCache.d.ts +1 -1
  21. package/dist/core/componentCache.d.ts.map +1 -1
  22. package/dist/core/componentCache.js +6 -4
  23. package/dist/core/entrypoint.d.ts +17 -7
  24. package/dist/core/entrypoint.d.ts.map +1 -1
  25. package/dist/core/garbageCollection.d.ts +8 -2
  26. package/dist/core/garbageCollection.d.ts.map +1 -1
  27. package/dist/core/garbageCollection.js +36 -14
  28. package/dist/core/logging.d.ts +16 -3
  29. package/dist/core/logging.d.ts.map +1 -1
  30. package/dist/core/makeNetworkRequest.d.ts +4 -2
  31. package/dist/core/makeNetworkRequest.d.ts.map +1 -1
  32. package/dist/core/makeNetworkRequest.js +115 -38
  33. package/dist/core/optimisticProxy.d.ts +59 -0
  34. package/dist/core/optimisticProxy.d.ts.map +1 -0
  35. package/dist/core/optimisticProxy.js +399 -0
  36. package/dist/core/read.d.ts +3 -2
  37. package/dist/core/read.d.ts.map +1 -1
  38. package/dist/core/read.js +158 -123
  39. package/dist/core/reader.d.ts +10 -5
  40. package/dist/core/reader.d.ts.map +1 -1
  41. package/dist/core/startUpdate.d.ts +3 -2
  42. package/dist/core/startUpdate.d.ts.map +1 -1
  43. package/dist/core/startUpdate.js +35 -36
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +2 -1
  47. package/dist/loadable-hooks/useClientSideDefer.d.ts +9 -4
  48. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -1
  49. package/dist/loadable-hooks/useClientSideDefer.js +34 -1
  50. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +5 -3
  51. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -1
  52. package/dist/loadable-hooks/useConnectionSpecPagination.js +27 -13
  53. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +1 -1
  54. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -1
  55. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +1 -1
  56. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -1
  57. package/dist/loadable-hooks/useSkipLimitPagination.js +1 -1
  58. package/dist/react/FragmentReader.d.ts +2 -1
  59. package/dist/react/FragmentReader.d.ts.map +1 -1
  60. package/dist/react/FragmentRenderer.d.ts +2 -1
  61. package/dist/react/FragmentRenderer.d.ts.map +1 -1
  62. package/dist/react/LoadableFieldReader.d.ts +9 -3
  63. package/dist/react/LoadableFieldReader.d.ts.map +1 -1
  64. package/dist/react/LoadableFieldReader.js +40 -1
  65. package/dist/react/LoadableFieldRenderer.d.ts +9 -3
  66. package/dist/react/LoadableFieldRenderer.d.ts.map +1 -1
  67. package/dist/react/LoadableFieldRenderer.js +36 -1
  68. package/dist/react/useImperativeReference.d.ts +4 -3
  69. package/dist/react/useImperativeReference.d.ts.map +1 -1
  70. package/dist/react/useImperativeReference.js +3 -5
  71. package/dist/react/useLazyReference.d.ts +2 -1
  72. package/dist/react/useLazyReference.d.ts.map +1 -1
  73. package/dist/react/useReadAndSubscribe.d.ts.map +1 -1
  74. package/dist/react/useReadAndSubscribe.js +1 -3
  75. package/dist/react/useResult.d.ts.map +1 -1
  76. package/dist/react/useResult.js +6 -5
  77. package/package.json +16 -17
  78. package/src/core/FragmentReference.ts +10 -4
  79. package/src/core/IsographEnvironment.ts +59 -13
  80. package/src/core/PromiseWrapper.ts +14 -7
  81. package/src/core/areEqualWithDeepComparison.ts +5 -0
  82. package/src/core/brand.ts +18 -0
  83. package/src/core/cache.ts +186 -91
  84. package/src/core/check.ts +21 -10
  85. package/src/core/componentCache.ts +8 -4
  86. package/src/core/entrypoint.ts +35 -6
  87. package/src/core/garbageCollection.ts +61 -19
  88. package/src/core/logging.ts +15 -3
  89. package/src/core/makeNetworkRequest.ts +307 -74
  90. package/src/core/optimisticProxy.ts +563 -0
  91. package/src/core/read.ts +233 -163
  92. package/src/core/reader.ts +11 -7
  93. package/src/core/startUpdate.ts +47 -32
  94. package/src/index.ts +2 -1
  95. package/src/loadable-hooks/useClientSideDefer.ts +76 -26
  96. package/src/loadable-hooks/useConnectionSpecPagination.ts +34 -17
  97. package/src/loadable-hooks/useImperativeLoadableField.ts +2 -2
  98. package/src/loadable-hooks/useSkipLimitPagination.ts +2 -3
  99. package/src/react/FragmentReader.tsx +3 -1
  100. package/src/react/FragmentRenderer.tsx +8 -1
  101. package/src/react/LoadableFieldReader.tsx +123 -12
  102. package/src/react/LoadableFieldRenderer.tsx +122 -12
  103. package/src/react/useImperativeReference.ts +20 -11
  104. package/src/react/useLazyReference.ts +17 -6
  105. package/src/react/useReadAndSubscribe.ts +1 -8
  106. package/src/react/useResult.ts +9 -11
  107. package/src/tests/__isograph/Node/__link/output_type.ts +3 -0
  108. package/src/tests/__isograph/Node/asEconomist/resolver_reader.ts +3 -3
  109. package/src/tests/__isograph/Query/linkedUpdate/entrypoint.ts +3 -1
  110. package/src/tests/__isograph/Query/linkedUpdate/param_type.ts +4 -4
  111. package/src/tests/__isograph/Query/linkedUpdate/raw_response_type.ts +13 -0
  112. package/src/tests/__isograph/Query/linkedUpdate/resolver_reader.ts +6 -6
  113. package/src/tests/__isograph/Query/meName/entrypoint.ts +3 -1
  114. package/src/tests/__isograph/Query/meName/raw_response_type.ts +7 -0
  115. package/src/tests/__isograph/Query/meName/resolver_reader.ts +2 -2
  116. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +3 -1
  117. package/src/tests/__isograph/Query/meNameSuccessor/raw_response_type.ts +14 -0
  118. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +4 -4
  119. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +3 -1
  120. package/src/tests/__isograph/Query/nodeField/raw_response_type.ts +7 -0
  121. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +2 -2
  122. package/src/tests/__isograph/Query/normalizeUndefinedField/entrypoint.ts +33 -0
  123. package/src/tests/__isograph/Query/normalizeUndefinedField/normalization_ast.ts +25 -0
  124. package/src/tests/__isograph/Query/normalizeUndefinedField/output_type.ts +3 -0
  125. package/src/tests/__isograph/Query/normalizeUndefinedField/param_type.ts +9 -0
  126. package/src/tests/__isograph/Query/normalizeUndefinedField/query_text.ts +6 -0
  127. package/src/tests/__isograph/Query/normalizeUndefinedField/raw_response_type.ts +7 -0
  128. package/src/tests/__isograph/Query/normalizeUndefinedField/resolver_reader.ts +38 -0
  129. package/src/tests/__isograph/Query/startUpdate/entrypoint.ts +3 -1
  130. package/src/tests/__isograph/Query/startUpdate/raw_response_type.ts +8 -0
  131. package/src/tests/__isograph/Query/startUpdate/resolver_reader.ts +3 -3
  132. package/src/tests/__isograph/Query/subquery/entrypoint.ts +3 -1
  133. package/src/tests/__isograph/Query/subquery/raw_response_type.ts +9 -0
  134. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +3 -3
  135. package/src/tests/__isograph/iso.ts +11 -1
  136. package/src/tests/garbageCollection.test.ts +8 -5
  137. package/src/tests/meNameSuccessor.ts +6 -3
  138. package/src/tests/nodeQuery.ts +4 -2
  139. package/src/tests/normalizeData.test.ts +89 -15
  140. package/src/tests/optimisticProxy.test.ts +860 -0
  141. package/src/tests/startUpdate.test.ts +8 -6
  142. package/vitest.config.ts +10 -1
  143. package/src/tests/__isograph/Economist/link/output_type.ts +0 -2
  144. package/src/tests/__isograph/Node/link/output_type.ts +0 -3
package/package.json CHANGED
@@ -1,27 +1,16 @@
1
1
  {
2
2
  "name": "@isograph/react",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Use Isograph with React",
5
5
  "homepage": "https://isograph.dev",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "author": "Isograph Labs",
9
9
  "license": "MIT",
10
- "scripts": {
11
- "compile-libs": "rimraf dist && tsc -p tsconfig.pkg.json",
12
- "compile-watch": "tsc -p tsconfig.pkg.json --watch",
13
- "test": "vitest run",
14
- "test-watch": "vitest watch",
15
- "coverage": "vitest run --coverage",
16
- "prepack": "pnpm run test && pnpm run compile-libs",
17
- "tsc": "tsc",
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
- },
21
10
  "dependencies": {
22
- "@isograph/disposable-types": "*",
23
- "@isograph/react-disposable-state": "*",
24
- "@isograph/reference-counted-pointer": "*"
11
+ "@isograph/disposable-types": "0.5.0",
12
+ "@isograph/react-disposable-state": "0.5.0",
13
+ "@isograph/reference-counted-pointer": "0.5.0"
25
14
  },
26
15
  "peerDependencies": {
27
16
  "react": "^18.0.0 || ^19.0.0"
@@ -39,5 +28,15 @@
39
28
  "url": "git+https://github.com/isographlabs/isograph.git",
40
29
  "directory": "libs/isograph-react"
41
30
  },
42
- "sideEffects": false
43
- }
31
+ "sideEffects": false,
32
+ "scripts": {
33
+ "compile-libs": "rimraf dist && tsc -p tsconfig.pkg.json",
34
+ "compile-watch": "tsc -p tsconfig.pkg.json --watch",
35
+ "test": "vitest run",
36
+ "test-watch": "vitest watch",
37
+ "coverage": "vitest run --coverage",
38
+ "tsc": "tsc",
39
+ "tsc-force": "tsc --build --clean && tsc --build --force",
40
+ "iso": "cross-env ISO_PRINT_ABSOLUTE_FILEPATH=1 ../../target/debug/isograph_cli --config ./isograph.config.json"
41
+ }
42
+ }
@@ -1,6 +1,9 @@
1
1
  import { ReaderWithRefetchQueries } from '../core/entrypoint';
2
2
  import { stableCopy } from './cache';
3
- import { type StoreLink } from './IsographEnvironment';
3
+ import {
4
+ type ComponentOrFieldName,
5
+ type StoreLink,
6
+ } from './IsographEnvironment';
4
7
  import { PromiseWrapper } from './PromiseWrapper';
5
8
  import type { StartUpdate } from './reader';
6
9
 
@@ -53,8 +56,12 @@ export type FragmentReference<
53
56
  ReaderWithRefetchQueries<TReadFromStore, TClientFieldValue>
54
57
  >;
55
58
  readonly root: StoreLink;
59
+ readonly fieldName: ComponentOrFieldName;
60
+ readonly readerArtifactKind:
61
+ | 'EagerReaderArtifact'
62
+ | 'ComponentReaderArtifact';
56
63
  // TODO we potentially stably copy and stringify variables a lot!
57
- // So, we should employ interior mutability: pretend that fragent reference
64
+ // So, we should employ interior mutability: pretend that fragment reference
58
65
  // is immutable, but actually store something like
59
66
  // `Map<Variable, StablyCopiedStringifiedOutput>`
60
67
  // and read or update that map when we would otherwise stably copy and
@@ -67,7 +74,6 @@ export type StableIdForFragmentReference = string;
67
74
 
68
75
  export function stableIdForFragmentReference(
69
76
  fragmentReference: FragmentReference<any, any>,
70
- fieldName: string,
71
77
  ): StableIdForFragmentReference {
72
- return `${fragmentReference.root.__typename}/${fragmentReference.root.__link}/${fieldName}/${JSON.stringify(stableCopy(fragmentReference.variables))}`;
78
+ return `${fragmentReference.root.__typename}/${fragmentReference.root.__link}/${fragmentReference.fieldName}/${JSON.stringify(stableCopy(fragmentReference.variables))}`;
73
79
  }
@@ -1,8 +1,12 @@
1
1
  import { ParentCache } from '@isograph/react-disposable-state';
2
+ import type { Brand } from './brand';
3
+ import { isArray } from './cache';
2
4
  import {
3
5
  IsographEntrypoint,
4
6
  IsographOperation,
5
7
  IsographPersistedOperation,
8
+ type ReaderWithRefetchQueries,
9
+ type ReaderWithRefetchQueriesLoader,
6
10
  } from './entrypoint';
7
11
  import {
8
12
  FragmentReference,
@@ -10,12 +14,16 @@ import {
10
14
  type StableIdForFragmentReference,
11
15
  type UnknownTReadFromStore,
12
16
  } from './FragmentReference';
13
- import { RetainedQuery } from './garbageCollection';
17
+ import type { RetainedQuery } from './garbageCollection';
14
18
  import { LogFunction, WrappedLogFunction } from './logging';
15
- import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
19
+ import { type StoreLayer } from './optimisticProxy';
20
+ import {
21
+ PromiseWrapper,
22
+ wrapPromise,
23
+ wrapResolvedValue,
24
+ } from './PromiseWrapper';
16
25
  import { WithEncounteredRecords } from './read';
17
26
  import type { ReaderAst, StartUpdate } from './reader';
18
- import type { Brand } from './brand';
19
27
 
20
28
  export type ComponentOrFieldName = string;
21
29
  export type StringifiedArgs = string;
@@ -56,7 +64,7 @@ export type Subscriptions = Set<Subscription>;
56
64
  export type CacheMap<T> = { [index: string]: ParentCache<T> };
57
65
 
58
66
  export type IsographEnvironment = {
59
- readonly store: IsographStore;
67
+ store: StoreLayer;
60
68
  readonly networkFunction: IsographNetworkFunction;
61
69
  readonly missingFieldHandler: MissingFieldHandler | null;
62
70
  readonly componentCache: FieldCache<React.FC<any>>;
@@ -68,7 +76,7 @@ export type IsographEnvironment = {
68
76
  // TODO make this a CacheMap and add GC
69
77
  readonly entrypointArtifactCache: Map<
70
78
  string,
71
- PromiseWrapper<IsographEntrypoint<any, any, any>>
79
+ PromiseWrapper<IsographEntrypoint<any, any, any, any>>
72
80
  >;
73
81
  readonly retainedQueries: Set<RetainedQuery>;
74
82
  readonly gcBuffer: Array<RetainedQuery>;
@@ -111,7 +119,7 @@ export type DataTypeValue =
111
119
  // Singular linked fields:
112
120
  | StoreLink
113
121
  // Plural scalar and linked fields:
114
- | DataTypeValue[];
122
+ | readonly DataTypeValue[];
115
123
 
116
124
  export type StoreRecord = {
117
125
  [index: DataId | string]: DataTypeValue;
@@ -125,18 +133,21 @@ export type DataId = string;
125
133
 
126
134
  export const ROOT_ID: DataId & '__ROOT' = '__ROOT';
127
135
 
128
- export type IsographStore = {
136
+ export type StoreLayerData = {
129
137
  [index: TypeName]: {
130
138
  [index: DataId]: StoreRecord | null;
131
139
  } | null;
140
+ };
141
+
142
+ export interface BaseStoreLayerData extends StoreLayerData {
132
143
  readonly Query: {
133
144
  readonly __ROOT: StoreRecord;
134
145
  };
135
- };
146
+ }
136
147
 
137
148
  const DEFAULT_GC_BUFFER_SIZE = 10;
138
149
  export function createIsographEnvironment(
139
- store: IsographStore,
150
+ baseStoreLayerData: BaseStoreLayerData,
140
151
  networkFunction: IsographNetworkFunction,
141
152
  missingFieldHandler?: MissingFieldHandler | null,
142
153
  logFunction?: LogFunction | null,
@@ -144,6 +155,12 @@ export function createIsographEnvironment(
144
155
  logFunction?.({
145
156
  kind: 'EnvironmentCreated',
146
157
  });
158
+ let store = {
159
+ kind: 'BaseStoreLayer',
160
+ data: baseStoreLayerData,
161
+ parentStoreLayer: null,
162
+ childStoreLayer: null,
163
+ } as const;
147
164
  return {
148
165
  store,
149
166
  networkFunction,
@@ -160,7 +177,7 @@ export function createIsographEnvironment(
160
177
  };
161
178
  }
162
179
 
163
- export function createIsographStore(): IsographStore {
180
+ export function createIsographStore(): BaseStoreLayerData {
164
181
  return {
165
182
  Query: {
166
183
  [ROOT_ID]: {},
@@ -169,7 +186,7 @@ export function createIsographStore(): IsographStore {
169
186
  }
170
187
 
171
188
  export function assertLink(link: DataTypeValue): StoreLink | null | undefined {
172
- if (Array.isArray(link)) {
189
+ if (isArray(link)) {
173
190
  throw new Error('Unexpected array');
174
191
  }
175
192
  if (link == null) {
@@ -198,8 +215,8 @@ export function getLink(maybeLink: DataTypeValue): StoreLink | null {
198
215
  export function getOrLoadIsographArtifact(
199
216
  environment: IsographEnvironment,
200
217
  key: string,
201
- loader: () => Promise<IsographEntrypoint<any, any, any>>,
202
- ): PromiseWrapper<IsographEntrypoint<any, any, any>> {
218
+ loader: () => Promise<IsographEntrypoint<any, any, any, any>>,
219
+ ): PromiseWrapper<IsographEntrypoint<any, any, any, any>> {
203
220
  const value = environment.entrypointArtifactCache.get(key);
204
221
  if (value != null) {
205
222
  return value;
@@ -208,3 +225,32 @@ export function getOrLoadIsographArtifact(
208
225
  environment.entrypointArtifactCache.set(key, wrapped);
209
226
  return wrapped;
210
227
  }
228
+
229
+ export function getOrLoadReaderWithRefetchQueries(
230
+ _environment: IsographEnvironment,
231
+ readerWithRefetchQueries:
232
+ | ReaderWithRefetchQueries<any, any>
233
+ | ReaderWithRefetchQueriesLoader<any, any>,
234
+ ): {
235
+ readerWithRefetchQueries: PromiseWrapper<ReaderWithRefetchQueries<any, any>>;
236
+ fieldName: string;
237
+ readerArtifactKind: 'EagerReaderArtifact' | 'ComponentReaderArtifact';
238
+ } {
239
+ switch (readerWithRefetchQueries.kind) {
240
+ case 'ReaderWithRefetchQueries':
241
+ return {
242
+ readerWithRefetchQueries: wrapResolvedValue(readerWithRefetchQueries),
243
+ fieldName: readerWithRefetchQueries.readerArtifact.fieldName,
244
+ readerArtifactKind: readerWithRefetchQueries.readerArtifact.kind,
245
+ };
246
+ case 'ReaderWithRefetchQueriesLoader':
247
+ return {
248
+ // TODO: cache promise wrapper
249
+ readerWithRefetchQueries: wrapPromise(
250
+ readerWithRefetchQueries.loader(),
251
+ ),
252
+ fieldName: readerWithRefetchQueries.fieldName,
253
+ readerArtifactKind: readerWithRefetchQueries.readerArtifactKind,
254
+ };
255
+ }
256
+ }
@@ -5,12 +5,12 @@ export type NotSet = typeof NOT_SET;
5
5
 
6
6
  export type Result<T, E> =
7
7
  | {
8
- kind: 'Ok';
9
- value: T;
8
+ readonly kind: 'Ok';
9
+ readonly value: T;
10
10
  }
11
11
  | {
12
- kind: 'Err';
13
- error: E;
12
+ readonly kind: 'Err';
13
+ readonly error: E;
14
14
  };
15
15
 
16
16
  /**
@@ -22,6 +22,13 @@ export type PromiseWrapper<T, E = any> = {
22
22
  result: Result<Exclude<T, NotSet>, E> | NotSet;
23
23
  };
24
24
 
25
+ export interface PromiseWrapperOk<T, E = any> extends PromiseWrapper<T, E> {
26
+ result: {
27
+ readonly kind: 'Ok';
28
+ readonly value: Exclude<T, NotSet>;
29
+ };
30
+ }
31
+
25
32
  export function wrapPromise<T>(
26
33
  promise: Promise<Exclude<T, NotSet>>,
27
34
  ): PromiseWrapper<T, unknown> {
@@ -39,7 +46,7 @@ export function wrapPromise<T>(
39
46
 
40
47
  export function wrapResolvedValue<T>(
41
48
  value: Exclude<T, NotSet>,
42
- ): PromiseWrapper<T, never> {
49
+ ): PromiseWrapperOk<T, never> {
43
50
  return {
44
51
  promise: Promise.resolve(value),
45
52
  result: {
@@ -65,8 +72,8 @@ export function readPromise<T, E>(p: PromiseWrapper<T, E>): T {
65
72
 
66
73
  export type PromiseState<T, E> =
67
74
  | {
68
- kind: 'Pending';
69
- promise: Promise<T>;
75
+ readonly kind: 'Pending';
76
+ readonly promise: Promise<T>;
70
77
  }
71
78
  | Result<T, E>;
72
79
 
@@ -72,6 +72,11 @@ export function mergeObjectsUsingReaderAst(
72
72
  switch (field.kind) {
73
73
  case 'Scalar':
74
74
  case 'Linked':
75
+ if (field.kind === 'Linked' && field.refetchQueryIndex != null) {
76
+ // client pointers are functions, so we can't merge them
77
+ canRecycle = false;
78
+ break;
79
+ }
75
80
  const key = field.alias ?? field.fieldName;
76
81
  // @ts-expect-error
77
82
  const oldValue = oldItemObject[key];
package/src/core/brand.ts CHANGED
@@ -16,3 +16,21 @@ export type Brand<
16
16
  Brand extends symbol | string,
17
17
  // @ts-ignore
18
18
  > = infer _ extends Brand ? BaseType : never;
19
+
20
+ declare const PhantomDataBrand: unique symbol;
21
+ /**
22
+ * A phantom data type is one that shouldn't show up at runtime, but is checked statically (and only) at compile time.
23
+ * Data types can use extra properties to act as markers or to perform type checking at compile time. These extra properties should hold no storage values, and have no runtime behavior.
24
+ *
25
+ * ```tsx
26
+ * type MyType<T> = {
27
+ * readonly value: number;
28
+ * readonly '~T'?: PhantomData<T>;
29
+ * }
30
+ * ```
31
+ */
32
+ export type PhantomData<T> = Brand<T, typeof PhantomDataBrand>;
33
+
34
+ export type Invariant<T> = (_: T) => T;
35
+ export type Covariant<T> = (_: never) => T;
36
+ export type Contravariant<T> = (_: T) => void;