@isograph/react 0.4.3 → 0.5.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 (327) hide show
  1. package/.turbo/turbo-compile-libs.log +10 -3
  2. package/dist/_virtual/rolldown_runtime.js +25 -0
  3. package/dist/core/FragmentReference.d.mts +38 -0
  4. package/dist/core/FragmentReference.d.mts.map +1 -0
  5. package/dist/core/FragmentReference.d.ts +31 -27
  6. package/dist/core/FragmentReference.d.ts.map +1 -1
  7. package/dist/core/FragmentReference.js +8 -6
  8. package/dist/core/FragmentReference.mjs +10 -0
  9. package/dist/core/FragmentReference.mjs.map +1 -0
  10. package/dist/core/IsographEnvironment.d.mts +89 -0
  11. package/dist/core/IsographEnvironment.d.mts.map +1 -0
  12. package/dist/core/IsographEnvironment.d.ts +77 -72
  13. package/dist/core/IsographEnvironment.d.ts.map +1 -1
  14. package/dist/core/IsographEnvironment.js +62 -59
  15. package/dist/core/IsographEnvironment.mjs +67 -0
  16. package/dist/core/IsographEnvironment.mjs.map +1 -0
  17. package/dist/core/PromiseWrapper.d.mts +36 -0
  18. package/dist/core/PromiseWrapper.d.mts.map +1 -0
  19. package/dist/core/PromiseWrapper.d.ts +27 -18
  20. package/dist/core/PromiseWrapper.d.ts.map +1 -1
  21. package/dist/core/PromiseWrapper.js +45 -43
  22. package/dist/core/PromiseWrapper.mjs +49 -0
  23. package/dist/core/PromiseWrapper.mjs.map +1 -0
  24. package/dist/core/areEqualWithDeepComparison.js +56 -108
  25. package/dist/core/areEqualWithDeepComparison.mjs +62 -0
  26. package/dist/core/areEqualWithDeepComparison.mjs.map +1 -0
  27. package/dist/core/brand.d.mts +19 -0
  28. package/dist/core/brand.d.mts.map +1 -0
  29. package/dist/core/brand.d.ts +18 -1
  30. package/dist/core/brand.d.ts.map +1 -1
  31. package/dist/core/cache.d.mts +20 -0
  32. package/dist/core/cache.d.mts.map +1 -0
  33. package/dist/core/cache.d.ts +18 -34
  34. package/dist/core/cache.d.ts.map +1 -1
  35. package/dist/core/cache.js +210 -516
  36. package/dist/core/cache.mjs +237 -0
  37. package/dist/core/cache.mjs.map +1 -0
  38. package/dist/core/check.d.mts +28 -0
  39. package/dist/core/check.d.mts.map +1 -0
  40. package/dist/core/check.d.ts +24 -18
  41. package/dist/core/check.d.ts.map +1 -1
  42. package/dist/core/check.js +80 -124
  43. package/dist/core/check.mjs +84 -0
  44. package/dist/core/check.mjs.map +1 -0
  45. package/dist/core/componentCache.js +10 -34
  46. package/dist/core/componentCache.mjs +12 -0
  47. package/dist/core/componentCache.mjs.map +1 -0
  48. package/dist/core/entrypoint.d.mts +99 -0
  49. package/dist/core/entrypoint.d.mts.map +1 -0
  50. package/dist/core/entrypoint.d.ts +83 -68
  51. package/dist/core/entrypoint.d.ts.map +1 -1
  52. package/dist/core/entrypoint.js +6 -5
  53. package/dist/core/entrypoint.mjs +8 -0
  54. package/dist/core/entrypoint.mjs.map +1 -0
  55. package/dist/core/garbageCollection.d.mts +18 -0
  56. package/dist/core/garbageCollection.d.mts.map +1 -0
  57. package/dist/core/garbageCollection.d.ts +15 -10
  58. package/dist/core/garbageCollection.d.ts.map +1 -1
  59. package/dist/core/garbageCollection.js +82 -99
  60. package/dist/core/garbageCollection.mjs +89 -0
  61. package/dist/core/garbageCollection.mjs.map +1 -0
  62. package/dist/core/getOrCreateCacheForArtifact.js +37 -0
  63. package/dist/core/getOrCreateCacheForArtifact.mjs +38 -0
  64. package/dist/core/getOrCreateCacheForArtifact.mjs.map +1 -0
  65. package/dist/core/logging.d.mts +95 -0
  66. package/dist/core/logging.d.mts.map +1 -0
  67. package/dist/core/logging.d.ts +78 -60
  68. package/dist/core/logging.d.ts.map +1 -1
  69. package/dist/core/logging.js +17 -18
  70. package/dist/core/logging.mjs +20 -0
  71. package/dist/core/logging.mjs.map +1 -0
  72. package/dist/core/makeNetworkRequest.d.mts +13 -0
  73. package/dist/core/makeNetworkRequest.d.mts.map +1 -0
  74. package/dist/core/makeNetworkRequest.d.ts +12 -8
  75. package/dist/core/makeNetworkRequest.d.ts.map +1 -1
  76. package/dist/core/makeNetworkRequest.js +188 -195
  77. package/dist/core/makeNetworkRequest.mjs +195 -0
  78. package/dist/core/makeNetworkRequest.mjs.map +1 -0
  79. package/dist/core/optimisticProxy.d.mts +43 -0
  80. package/dist/core/optimisticProxy.d.mts.map +1 -0
  81. package/dist/core/optimisticProxy.d.ts +43 -0
  82. package/dist/core/optimisticProxy.d.ts.map +1 -0
  83. package/dist/core/optimisticProxy.js +273 -0
  84. package/dist/core/optimisticProxy.mjs +268 -0
  85. package/dist/core/optimisticProxy.mjs.map +1 -0
  86. package/dist/core/read.d.mts +29 -0
  87. package/dist/core/read.d.mts.map +1 -0
  88. package/dist/core/read.d.ts +23 -27
  89. package/dist/core/read.d.ts.map +1 -1
  90. package/dist/core/read.js +438 -618
  91. package/dist/core/read.mjs +456 -0
  92. package/dist/core/read.mjs.map +1 -0
  93. package/dist/core/reader.d.mts +89 -0
  94. package/dist/core/reader.d.mts.map +1 -0
  95. package/dist/core/reader.d.ts +78 -74
  96. package/dist/core/reader.d.ts.map +1 -1
  97. package/dist/core/startUpdate.js +118 -156
  98. package/dist/core/startUpdate.mjs +125 -0
  99. package/dist/core/startUpdate.mjs.map +1 -0
  100. package/dist/core/subscribe.d.mts +12 -0
  101. package/dist/core/subscribe.d.mts.map +1 -0
  102. package/dist/core/subscribe.d.ts +12 -0
  103. package/dist/core/subscribe.d.ts.map +1 -0
  104. package/dist/core/subscribe.js +79 -0
  105. package/dist/core/subscribe.mjs +79 -0
  106. package/dist/core/subscribe.mjs.map +1 -0
  107. package/dist/core/util.d.mts +27 -0
  108. package/dist/core/util.d.mts.map +1 -0
  109. package/dist/core/util.d.ts +21 -17
  110. package/dist/core/util.d.ts.map +1 -1
  111. package/dist/core/util.js +22 -2
  112. package/dist/core/util.mjs +21 -0
  113. package/dist/core/util.mjs.map +1 -0
  114. package/dist/core/writeData.d.mts +11 -0
  115. package/dist/core/writeData.d.mts.map +1 -0
  116. package/dist/core/writeData.d.ts +11 -0
  117. package/dist/core/writeData.d.ts.map +1 -0
  118. package/dist/core/writeData.js +41 -0
  119. package/dist/core/writeData.mjs +42 -0
  120. package/dist/core/writeData.mjs.map +1 -0
  121. package/dist/index.d.mts +32 -0
  122. package/dist/index.d.ts +32 -29
  123. package/dist/index.js +70 -68
  124. package/dist/index.mjs +31 -0
  125. package/dist/loadable-hooks/useClientSideDefer.d.mts +12 -0
  126. package/dist/loadable-hooks/useClientSideDefer.d.mts.map +1 -0
  127. package/dist/loadable-hooks/useClientSideDefer.d.ts +10 -8
  128. package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -1
  129. package/dist/loadable-hooks/useClientSideDefer.js +14 -12
  130. package/dist/loadable-hooks/useClientSideDefer.mjs +14 -0
  131. package/dist/loadable-hooks/useClientSideDefer.mjs.map +1 -0
  132. package/dist/loadable-hooks/useConnectionSpecPagination.d.mts +33 -0
  133. package/dist/loadable-hooks/useConnectionSpecPagination.d.mts.map +1 -0
  134. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +27 -21
  135. package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -1
  136. package/dist/loadable-hooks/useConnectionSpecPagination.js +133 -158
  137. package/dist/loadable-hooks/useConnectionSpecPagination.mjs +134 -0
  138. package/dist/loadable-hooks/useConnectionSpecPagination.mjs.map +1 -0
  139. package/dist/loadable-hooks/useImperativeExposedMutationField.d.mts +8 -0
  140. package/dist/loadable-hooks/useImperativeExposedMutationField.d.mts.map +1 -0
  141. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +6 -3
  142. package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -1
  143. package/dist/loadable-hooks/useImperativeExposedMutationField.js +9 -12
  144. package/dist/loadable-hooks/useImperativeExposedMutationField.mjs +11 -0
  145. package/dist/loadable-hooks/useImperativeExposedMutationField.mjs.map +1 -0
  146. package/dist/loadable-hooks/useImperativeLoadableField.d.mts +19 -0
  147. package/dist/loadable-hooks/useImperativeLoadableField.d.mts.map +1 -0
  148. package/dist/loadable-hooks/useImperativeLoadableField.d.ts +15 -11
  149. package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -1
  150. package/dist/loadable-hooks/useImperativeLoadableField.js +16 -12
  151. package/dist/loadable-hooks/useImperativeLoadableField.mjs +17 -0
  152. package/dist/loadable-hooks/useImperativeLoadableField.mjs.map +1 -0
  153. package/dist/loadable-hooks/useSkipLimitPagination.d.mts +24 -0
  154. package/dist/loadable-hooks/useSkipLimitPagination.d.mts.map +1 -0
  155. package/dist/loadable-hooks/useSkipLimitPagination.d.ts +19 -15
  156. package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -1
  157. package/dist/loadable-hooks/useSkipLimitPagination.js +118 -160
  158. package/dist/loadable-hooks/useSkipLimitPagination.mjs +119 -0
  159. package/dist/loadable-hooks/useSkipLimitPagination.mjs.map +1 -0
  160. package/dist/react/FragmentReader.d.mts +18 -0
  161. package/dist/react/FragmentReader.d.mts.map +1 -0
  162. package/dist/react/FragmentReader.d.ts +16 -7
  163. package/dist/react/FragmentReader.d.ts.map +1 -1
  164. package/dist/react/FragmentReader.js +8 -7
  165. package/dist/react/FragmentReader.mjs +10 -0
  166. package/dist/react/FragmentReader.mjs.map +1 -0
  167. package/dist/react/FragmentRenderer.d.mts +20 -0
  168. package/dist/react/FragmentRenderer.d.mts.map +1 -0
  169. package/dist/react/FragmentRenderer.d.ts +18 -13
  170. package/dist/react/FragmentRenderer.d.ts.map +1 -1
  171. package/dist/react/FragmentRenderer.js +11 -33
  172. package/dist/react/FragmentRenderer.mjs +12 -0
  173. package/dist/react/FragmentRenderer.mjs.map +1 -0
  174. package/dist/react/IsographEnvironmentProvider.d.mts +17 -0
  175. package/dist/react/IsographEnvironmentProvider.d.mts.map +1 -0
  176. package/dist/react/IsographEnvironmentProvider.d.ts +15 -9
  177. package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -1
  178. package/dist/react/IsographEnvironmentProvider.js +15 -39
  179. package/dist/react/IsographEnvironmentProvider.mjs +17 -0
  180. package/dist/react/IsographEnvironmentProvider.mjs.map +1 -0
  181. package/dist/react/LoadableFieldReader.d.mts +21 -0
  182. package/dist/react/LoadableFieldReader.d.mts.map +1 -0
  183. package/dist/react/LoadableFieldReader.d.ts +20 -11
  184. package/dist/react/LoadableFieldReader.d.ts.map +1 -1
  185. package/dist/react/LoadableFieldReader.js +13 -8
  186. package/dist/react/LoadableFieldReader.mjs +14 -0
  187. package/dist/react/LoadableFieldReader.mjs.map +1 -0
  188. package/dist/react/LoadableFieldRenderer.d.mts +22 -0
  189. package/dist/react/LoadableFieldRenderer.d.mts.map +1 -0
  190. package/dist/react/LoadableFieldRenderer.d.ts +21 -12
  191. package/dist/react/LoadableFieldRenderer.d.ts.map +1 -1
  192. package/dist/react/LoadableFieldRenderer.js +13 -35
  193. package/dist/react/LoadableFieldRenderer.mjs +14 -0
  194. package/dist/react/LoadableFieldRenderer.mjs.map +1 -0
  195. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.mts +18 -0
  196. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.mts.map +1 -0
  197. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +11 -3
  198. package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -1
  199. package/dist/react/RenderAfterCommit__DO_NOT_USE.js +17 -13
  200. package/dist/react/RenderAfterCommit__DO_NOT_USE.mjs +18 -0
  201. package/dist/react/RenderAfterCommit__DO_NOT_USE.mjs.map +1 -0
  202. package/dist/react/createIsographEnvironment.d.mts +9 -0
  203. package/dist/react/createIsographEnvironment.d.mts.map +1 -0
  204. package/dist/react/createIsographEnvironment.d.ts +8 -0
  205. package/dist/react/createIsographEnvironment.d.ts.map +1 -0
  206. package/dist/react/createIsographEnvironment.js +10 -0
  207. package/dist/react/createIsographEnvironment.mjs +11 -0
  208. package/dist/react/createIsographEnvironment.mjs.map +1 -0
  209. package/dist/react/maybeUnwrapNetworkRequest.js +11 -0
  210. package/dist/react/maybeUnwrapNetworkRequest.mjs +12 -0
  211. package/dist/react/maybeUnwrapNetworkRequest.mjs.map +1 -0
  212. package/dist/react/useImperativeReference.d.mts +15 -0
  213. package/dist/react/useImperativeReference.d.mts.map +1 -0
  214. package/dist/react/useImperativeReference.d.ts +13 -7
  215. package/dist/react/useImperativeReference.d.ts.map +1 -1
  216. package/dist/react/useImperativeReference.js +34 -32
  217. package/dist/react/useImperativeReference.mjs +35 -0
  218. package/dist/react/useImperativeReference.mjs.map +1 -0
  219. package/dist/react/useLazyReference.d.mts +13 -0
  220. package/dist/react/useLazyReference.d.mts.map +1 -0
  221. package/dist/react/useLazyReference.d.ts +11 -5
  222. package/dist/react/useLazyReference.d.ts.map +1 -1
  223. package/dist/react/useLazyReference.js +17 -40
  224. package/dist/react/useLazyReference.mjs +18 -0
  225. package/dist/react/useLazyReference.mjs.map +1 -0
  226. package/dist/react/useReadAndSubscribe.d.mts +20 -0
  227. package/dist/react/useReadAndSubscribe.d.mts.map +1 -0
  228. package/dist/react/useReadAndSubscribe.d.ts +14 -9
  229. package/dist/react/useReadAndSubscribe.d.ts.map +1 -1
  230. package/dist/react/useReadAndSubscribe.js +57 -39
  231. package/dist/react/useReadAndSubscribe.mjs +59 -0
  232. package/dist/react/useReadAndSubscribe.mjs.map +1 -0
  233. package/dist/react/useRerenderOnChange.d.mts +12 -0
  234. package/dist/react/useRerenderOnChange.d.mts.map +1 -0
  235. package/dist/react/useRerenderOnChange.d.ts +10 -6
  236. package/dist/react/useRerenderOnChange.d.ts.map +1 -1
  237. package/dist/react/useRerenderOnChange.js +16 -20
  238. package/dist/react/useRerenderOnChange.mjs +17 -0
  239. package/dist/react/useRerenderOnChange.mjs.map +1 -0
  240. package/dist/react/useResult.d.mts +8 -0
  241. package/dist/react/useResult.d.mts.map +1 -0
  242. package/dist/react/useResult.d.ts +7 -5
  243. package/dist/react/useResult.d.ts.map +1 -1
  244. package/dist/react/useResult.js +27 -39
  245. package/dist/react/useResult.mjs +30 -0
  246. package/dist/react/useResult.mjs.map +1 -0
  247. package/package.json +26 -19
  248. package/src/core/FragmentReference.ts +13 -7
  249. package/src/core/IsographEnvironment.ts +80 -21
  250. package/src/core/PromiseWrapper.ts +14 -7
  251. package/src/core/areEqualWithDeepComparison.ts +2 -18
  252. package/src/core/brand.ts +18 -0
  253. package/src/core/cache.ts +62 -332
  254. package/src/core/check.ts +30 -26
  255. package/src/core/componentCache.ts +11 -44
  256. package/src/core/entrypoint.ts +50 -9
  257. package/src/core/garbageCollection.ts +81 -28
  258. package/src/core/getOrCreateCacheForArtifact.ts +86 -0
  259. package/src/core/logging.ts +25 -13
  260. package/src/core/makeNetworkRequest.ts +320 -94
  261. package/src/core/optimisticProxy.ts +544 -0
  262. package/src/core/read.ts +251 -198
  263. package/src/core/reader.ts +18 -17
  264. package/src/core/startUpdate.ts +47 -36
  265. package/src/core/subscribe.ts +189 -0
  266. package/src/core/util.ts +26 -0
  267. package/src/core/writeData.ts +79 -0
  268. package/src/index.ts +6 -4
  269. package/src/loadable-hooks/useClientSideDefer.ts +80 -30
  270. package/src/loadable-hooks/useConnectionSpecPagination.ts +54 -35
  271. package/src/loadable-hooks/useImperativeLoadableField.ts +5 -5
  272. package/src/loadable-hooks/useSkipLimitPagination.ts +22 -21
  273. package/src/react/FragmentReader.tsx +3 -1
  274. package/src/react/FragmentRenderer.tsx +8 -1
  275. package/src/react/IsographEnvironmentProvider.tsx +2 -1
  276. package/src/react/LoadableFieldReader.tsx +125 -16
  277. package/src/react/LoadableFieldRenderer.tsx +124 -16
  278. package/src/react/createIsographEnvironment.ts +23 -0
  279. package/src/react/maybeUnwrapNetworkRequest.ts +17 -0
  280. package/src/react/useImperativeReference.ts +25 -15
  281. package/src/react/useLazyReference.ts +20 -10
  282. package/src/react/useReadAndSubscribe.ts +53 -12
  283. package/src/react/useRerenderOnChange.ts +3 -3
  284. package/src/react/useResult.ts +15 -35
  285. package/src/tests/__isograph/Node/asEconomist/resolver_reader.ts +1 -1
  286. package/src/tests/__isograph/Query/linkedUpdate/entrypoint.ts +3 -1
  287. package/src/tests/__isograph/Query/linkedUpdate/raw_response_type.ts +13 -0
  288. package/src/tests/__isograph/Query/linkedUpdate/resolver_reader.ts +1 -1
  289. package/src/tests/__isograph/Query/meName/entrypoint.ts +3 -1
  290. package/src/tests/__isograph/Query/meName/raw_response_type.ts +7 -0
  291. package/src/tests/__isograph/Query/meName/resolver_reader.ts +1 -1
  292. package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +3 -1
  293. package/src/tests/__isograph/Query/meNameSuccessor/raw_response_type.ts +14 -0
  294. package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +1 -1
  295. package/src/tests/__isograph/Query/nodeField/entrypoint.ts +3 -1
  296. package/src/tests/__isograph/Query/nodeField/raw_response_type.ts +7 -0
  297. package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +1 -1
  298. package/src/tests/__isograph/Query/normalizeUndefinedField/entrypoint.ts +33 -0
  299. package/src/tests/__isograph/Query/normalizeUndefinedField/normalization_ast.ts +25 -0
  300. package/src/tests/__isograph/Query/normalizeUndefinedField/output_type.ts +3 -0
  301. package/src/tests/__isograph/Query/normalizeUndefinedField/param_type.ts +9 -0
  302. package/src/tests/__isograph/Query/normalizeUndefinedField/query_text.ts +6 -0
  303. package/src/tests/__isograph/Query/normalizeUndefinedField/raw_response_type.ts +7 -0
  304. package/src/tests/__isograph/Query/normalizeUndefinedField/resolver_reader.ts +38 -0
  305. package/src/tests/__isograph/Query/startUpdate/entrypoint.ts +3 -1
  306. package/src/tests/__isograph/Query/startUpdate/raw_response_type.ts +8 -0
  307. package/src/tests/__isograph/Query/startUpdate/resolver_reader.ts +1 -1
  308. package/src/tests/__isograph/Query/subquery/entrypoint.ts +3 -1
  309. package/src/tests/__isograph/Query/subquery/raw_response_type.ts +9 -0
  310. package/src/tests/__isograph/Query/subquery/resolver_reader.ts +1 -1
  311. package/src/tests/__isograph/iso.ts +11 -1
  312. package/src/tests/garbageCollection.test.ts +10 -10
  313. package/src/tests/meNameSuccessor.ts +7 -4
  314. package/src/tests/nodeQuery.ts +6 -4
  315. package/src/tests/normalizeData.test.ts +94 -18
  316. package/src/tests/optimisticProxy.test.ts +862 -0
  317. package/src/tests/startUpdate.test.ts +11 -11
  318. package/vitest.config.ts +5 -0
  319. package/dist/core/areEqualWithDeepComparison.d.ts +0 -3
  320. package/dist/core/areEqualWithDeepComparison.d.ts.map +0 -1
  321. package/dist/core/brand.js +0 -2
  322. package/dist/core/componentCache.d.ts +0 -5
  323. package/dist/core/componentCache.d.ts.map +0 -1
  324. package/dist/core/reader.js +0 -2
  325. package/dist/core/startUpdate.d.ts +0 -8
  326. package/dist/core/startUpdate.d.ts.map +0 -1
  327. package/dist/index.d.ts.map +0 -1
@@ -0,0 +1,544 @@
1
+ import { insertEmptySetIfMissing, type EncounteredIds } from './cache';
2
+ import { callSubscriptions } from './subscribe';
3
+ import type {
4
+ BaseStoreLayerData,
5
+ IsographEnvironment,
6
+ StoreLayerData,
7
+ StoreLink,
8
+ StoreRecord,
9
+ } from './IsographEnvironment';
10
+
11
+ export function getOrInsertRecord(
12
+ dataLayer: StoreLayerData,
13
+ link: StoreLink,
14
+ ): StoreRecord {
15
+ const recordsById = (dataLayer[link.__typename] ??= {});
16
+ return (recordsById[link.__link] ??= {});
17
+ }
18
+
19
+ /**
20
+ * Given the child-most store layer (i.e. environment.store) and a link (identifying a
21
+ * store record), create a proxy object that attempts to read through each successive
22
+ * store layer until a value (i.e. field name) is found. If found, return that value.
23
+ */
24
+ export function getStoreRecordProxy(
25
+ storeLayer: StoreLayer,
26
+ link: StoreLink,
27
+ ): Readonly<StoreRecord> | null | undefined {
28
+ let startNode: StoreLayer | null = storeLayer;
29
+ while (startNode != null) {
30
+ const storeRecord = startNode.data[link.__typename]?.[link.__link];
31
+ if (storeRecord === undefined) {
32
+ startNode = startNode.parentStoreLayer;
33
+ continue;
34
+ }
35
+
36
+ if (storeRecord == null) {
37
+ return null;
38
+ }
39
+
40
+ return getMutableStoreRecordProxy(startNode, link);
41
+ }
42
+
43
+ return undefined;
44
+ }
45
+
46
+ export function getMutableStoreRecordProxy(
47
+ childMostStoreLayer: StoreLayer,
48
+ link: StoreLink,
49
+ ): StoreRecord {
50
+ return new Proxy<StoreRecord>(
51
+ {},
52
+ {
53
+ get(_, propertyName) {
54
+ let currentStoreLayer: StoreLayer | null = childMostStoreLayer;
55
+ while (currentStoreLayer != null) {
56
+ const storeRecord =
57
+ currentStoreLayer.data[link.__typename]?.[link.__link];
58
+ if (storeRecord !== undefined) {
59
+ if (storeRecord == null) {
60
+ return undefined;
61
+ }
62
+ const value = Reflect.get(storeRecord, propertyName);
63
+ if (value !== undefined) {
64
+ return value;
65
+ }
66
+ }
67
+ currentStoreLayer = currentStoreLayer.parentStoreLayer;
68
+ }
69
+ },
70
+ has(_, propertyName) {
71
+ let currentStoreLayer: StoreLayer | null = childMostStoreLayer;
72
+ while (currentStoreLayer != null) {
73
+ const storeRecord =
74
+ currentStoreLayer.data[link.__typename]?.[link.__link];
75
+ if (storeRecord !== undefined) {
76
+ if (storeRecord == null) {
77
+ return false;
78
+ }
79
+
80
+ const value = Reflect.has(storeRecord, propertyName);
81
+ if (value) {
82
+ return true;
83
+ }
84
+ }
85
+ currentStoreLayer = currentStoreLayer.parentStoreLayer;
86
+ }
87
+ return false;
88
+ },
89
+ set(_, p, newValue) {
90
+ return Reflect.set(
91
+ getOrInsertRecord(childMostStoreLayer.data, link),
92
+ p,
93
+ newValue,
94
+ );
95
+ },
96
+ },
97
+ );
98
+ }
99
+
100
+ export type BaseStoreLayer = {
101
+ readonly kind: 'BaseStoreLayer';
102
+ childStoreLayer: OptimisticStoreLayer | null;
103
+ readonly parentStoreLayer: null;
104
+ readonly data: BaseStoreLayerData;
105
+ };
106
+
107
+ export type NetworkResponseStoreLayer = {
108
+ readonly kind: 'NetworkResponseStoreLayer';
109
+ childStoreLayer: OptimisticStoreLayer | StartUpdateStoreLayer | null;
110
+ parentStoreLayer: OptimisticStoreLayer | StartUpdateStoreLayer;
111
+ readonly data: StoreLayerData;
112
+ };
113
+
114
+ export type DataUpdate<TStoreLayer extends StoreLayer> = (
115
+ storeLayer: TStoreLayer,
116
+ ) => void;
117
+
118
+ export type StartUpdateStoreLayer = {
119
+ readonly kind: 'StartUpdateStoreLayer';
120
+ childStoreLayer: OptimisticStoreLayer | NetworkResponseStoreLayer | null;
121
+ parentStoreLayer: OptimisticStoreLayer | NetworkResponseStoreLayer;
122
+ data: StoreLayerData;
123
+ startUpdate: DataUpdate<StartUpdateStoreLayer | BaseStoreLayer>;
124
+ };
125
+
126
+ export type OptimisticStoreLayer =
127
+ | OptimisticUpdaterStoreLayer
128
+ | OptimisticNetworkResponseStoreLayer;
129
+
130
+ export type OptimisticUpdaterStoreLayer = {
131
+ readonly kind: 'OptimisticUpdaterStoreLayer';
132
+ childStoreLayer:
133
+ | OptimisticStoreLayer
134
+ | StartUpdateStoreLayer
135
+ | NetworkResponseStoreLayer
136
+ | null;
137
+ parentStoreLayer:
138
+ | OptimisticStoreLayer
139
+ | StartUpdateStoreLayer
140
+ | NetworkResponseStoreLayer
141
+ | BaseStoreLayer;
142
+ data: StoreLayerData;
143
+ readonly startUpdate: DataUpdate<OptimisticUpdaterStoreLayer>;
144
+ };
145
+
146
+ export type OptimisticNetworkResponseStoreLayer = {
147
+ readonly kind: 'OptimisticNetworkResponseStoreLayer';
148
+ childStoreLayer:
149
+ | OptimisticStoreLayer
150
+ | StartUpdateStoreLayer
151
+ | NetworkResponseStoreLayer
152
+ | null;
153
+ parentStoreLayer:
154
+ | OptimisticStoreLayer
155
+ | StartUpdateStoreLayer
156
+ | NetworkResponseStoreLayer
157
+ | BaseStoreLayer;
158
+ data: StoreLayerData;
159
+ };
160
+
161
+ export function addNetworkResponseStoreLayer(
162
+ parent: StoreLayer,
163
+ ): StoreLayerWithData {
164
+ switch (parent.kind) {
165
+ case 'NetworkResponseStoreLayer':
166
+ case 'BaseStoreLayer': {
167
+ return parent;
168
+ }
169
+ case 'StartUpdateStoreLayer':
170
+ case 'OptimisticNetworkResponseStoreLayer':
171
+ case 'OptimisticUpdaterStoreLayer': {
172
+ const node: NetworkResponseStoreLayer = {
173
+ kind: 'NetworkResponseStoreLayer',
174
+ parentStoreLayer: parent,
175
+ childStoreLayer: null,
176
+ data: {},
177
+ };
178
+ parent.childStoreLayer = node;
179
+
180
+ return node;
181
+ }
182
+ }
183
+ }
184
+
185
+ function mergeDataLayer(target: StoreLayerData, source: StoreLayerData): void {
186
+ for (const [typeName, sourceById] of Object.entries(source)) {
187
+ if (sourceById == null) {
188
+ target[typeName] = sourceById;
189
+ continue;
190
+ }
191
+ const targetRecordById = (target[typeName] ??= {});
192
+ for (const [id, sourceRecord] of Object.entries(sourceById)) {
193
+ if (sourceRecord == null) {
194
+ targetRecordById[id] = null;
195
+ continue;
196
+ }
197
+ const targetRecord = (targetRecordById[id] ??= {});
198
+ Object.assign(targetRecord, sourceRecord);
199
+ }
200
+ }
201
+ }
202
+
203
+ export function addStartUpdateStoreLayer(
204
+ parent: StoreLayer,
205
+ startUpdate: StartUpdateStoreLayer['startUpdate'],
206
+ ): StoreLayer {
207
+ switch (parent.kind) {
208
+ case 'BaseStoreLayer': {
209
+ startUpdate(parent);
210
+ return parent;
211
+ }
212
+ case 'StartUpdateStoreLayer': {
213
+ const node = parent;
214
+
215
+ const prevStartUpdate = node.startUpdate;
216
+ node.startUpdate = () => {
217
+ prevStartUpdate(node);
218
+ startUpdate(node);
219
+ };
220
+
221
+ startUpdate(node);
222
+ return node;
223
+ }
224
+ case 'NetworkResponseStoreLayer':
225
+ case 'OptimisticNetworkResponseStoreLayer':
226
+ case 'OptimisticUpdaterStoreLayer': {
227
+ const node: StartUpdateStoreLayer = {
228
+ kind: 'StartUpdateStoreLayer',
229
+ parentStoreLayer: parent,
230
+ childStoreLayer: null,
231
+ data: {},
232
+ startUpdate: startUpdate,
233
+ };
234
+ parent.childStoreLayer = node;
235
+
236
+ startUpdate(node);
237
+ return node;
238
+ }
239
+ }
240
+ }
241
+
242
+ export function addOptimisticUpdaterStoreLayer(
243
+ parent: StoreLayer,
244
+ startUpdate: OptimisticUpdaterStoreLayer['startUpdate'],
245
+ ): OptimisticUpdaterStoreLayer {
246
+ switch (parent.kind) {
247
+ case 'BaseStoreLayer':
248
+ case 'StartUpdateStoreLayer':
249
+ case 'NetworkResponseStoreLayer':
250
+ case 'OptimisticNetworkResponseStoreLayer':
251
+ case 'OptimisticUpdaterStoreLayer': {
252
+ const node: OptimisticUpdaterStoreLayer = {
253
+ kind: 'OptimisticUpdaterStoreLayer',
254
+ parentStoreLayer: parent,
255
+ childStoreLayer: null,
256
+ data: {},
257
+ startUpdate: startUpdate,
258
+ };
259
+
260
+ startUpdate(node);
261
+ parent.childStoreLayer = node;
262
+
263
+ return node;
264
+ }
265
+ }
266
+ }
267
+
268
+ export function addOptimisticNetworkResponseStoreLayer(
269
+ parent: StoreLayer,
270
+ ): OptimisticNetworkResponseStoreLayer {
271
+ switch (parent.kind) {
272
+ case 'BaseStoreLayer':
273
+ case 'StartUpdateStoreLayer':
274
+ case 'NetworkResponseStoreLayer':
275
+ case 'OptimisticNetworkResponseStoreLayer':
276
+ case 'OptimisticUpdaterStoreLayer': {
277
+ const node: OptimisticNetworkResponseStoreLayer = {
278
+ kind: 'OptimisticNetworkResponseStoreLayer',
279
+ parentStoreLayer: parent,
280
+ childStoreLayer: null,
281
+ data: {},
282
+ };
283
+
284
+ parent.childStoreLayer = node;
285
+
286
+ return node;
287
+ }
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Merge storeLayerToMerge, and its children, into baseStoreLayer.
293
+ * We can merge until we reach a revertible layer (i.e. an optimistic layer).
294
+ * All other layers cannot be reverted, so for housekeeping + perf, we merge
295
+ * them into a single layer.
296
+ *
297
+ * Note that BaseStoreLayer.childStoreLayer has type OptimisticStoreLayer | null.
298
+ * So, the state of the stack is never e.g. base <- network response. Instead,
299
+ * we have a base + a child that we would like to attach to the base. So, we merge
300
+ * (flatten) until we reach an optimistic layer or null, at which point, we can
301
+ * set baseStoreLayer.childStoreLayer = storeLayerToMerge (via setChildOfNode).
302
+ */
303
+ function mergeLayersWithDataIntoBaseLayer(
304
+ environment: IsographEnvironment,
305
+ storeLayerToMerge: StoreLayer | null,
306
+ baseStoreLayer: BaseStoreLayer,
307
+ ) {
308
+ while (
309
+ storeLayerToMerge != null &&
310
+ storeLayerToMerge.kind !== 'OptimisticUpdaterStoreLayer'
311
+ ) {
312
+ mergeDataLayer(baseStoreLayer.data, storeLayerToMerge.data);
313
+ storeLayerToMerge = storeLayerToMerge.childStoreLayer;
314
+ }
315
+ setChildOfNode(environment, baseStoreLayer, storeLayerToMerge);
316
+ }
317
+
318
+ /**
319
+ * Now that we have replaced the optimistic layer with a network response layer, we need
320
+ * to
321
+ * - re-execute startUpdate and optimistic nodes, in light of the replaced data, and
322
+ * - create two objects containing the old merged data (from the optimistic update layer
323
+ * onward) and the new merged data (from the network response layer onward).
324
+ * - we will compare the new and old merged data in order to determine the changed records
325
+ * and trigger subscriptions.
326
+ *
327
+ * Here, "merged data" means all of the records + fields that were modified, starting at
328
+ * storeLayer, e.g. in BaseLayer <- OptimisticLayer <- StartUpdateLayer, if we
329
+ * are replacing Optimistic, then oldData will contain the records + fields modified by
330
+ * OptimisticLayer + StartUpdateLayer.
331
+ */
332
+ function reexecuteUpdatesAndMergeData(
333
+ storeLayer:
334
+ | OptimisticStoreLayer
335
+ | NetworkResponseStoreLayer
336
+ | StartUpdateStoreLayer
337
+ | null,
338
+ // reflects the (now reverted) optimistic layer
339
+ oldMergedData: StoreLayerData,
340
+ // reflects whatever replaced the optimistic layer
341
+ newMergedData: StoreLayerData,
342
+ ): void {
343
+ while (storeLayer != null) {
344
+ mergeDataLayer(oldMergedData, storeLayer.data);
345
+ switch (storeLayer.kind) {
346
+ case 'OptimisticNetworkResponseStoreLayer':
347
+ case 'NetworkResponseStoreLayer':
348
+ break;
349
+ case 'StartUpdateStoreLayer': {
350
+ storeLayer.data = {};
351
+ storeLayer.startUpdate(storeLayer);
352
+ break;
353
+ }
354
+ case 'OptimisticUpdaterStoreLayer': {
355
+ storeLayer.data = {};
356
+ storeLayer.startUpdate(storeLayer);
357
+ break;
358
+ }
359
+ }
360
+ mergeDataLayer(newMergedData, storeLayer.data);
361
+
362
+ storeLayer = storeLayer.childStoreLayer;
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Set storeLayerToModify's child to a given layer. This may be null!
368
+ * If it is null, set the environment.store to storeLayerToModify.
369
+ * If it is not null, then the existing environment.store value remains
370
+ * valid.
371
+ */
372
+ function setChildOfNode<TStoreLayer extends StoreLayer>(
373
+ environment: IsographEnvironment,
374
+ storeLayerToModify: TStoreLayer,
375
+ newChildStoreLayer: TStoreLayer['childStoreLayer'],
376
+ ) {
377
+ storeLayerToModify.childStoreLayer = newChildStoreLayer;
378
+ if (newChildStoreLayer != null) {
379
+ newChildStoreLayer.parentStoreLayer = storeLayerToModify;
380
+ } else {
381
+ environment.store = storeLayerToModify;
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Remove an optimistic store layer from the stack, potentially replacing it
387
+ * with a network response.
388
+ *
389
+ * After we do this, we must re-execute all child startUpdate and optimistic
390
+ * layers (since their data may have changed.) We also keep track of changed
391
+ * records, in order to call affected subscriptions.
392
+ */
393
+ export function revertOptimisticStoreLayerAndMaybeReplace(
394
+ environment: IsographEnvironment,
395
+ optimisticNode: OptimisticStoreLayer,
396
+ normalizeData: null | ((storeLayer: StoreLayerWithData) => void),
397
+ ): void {
398
+ // We cannot just replace the optimistic node with the network response node,
399
+ // because (e.g.) the types allow Base <- Opt, but not Base <- NetworkResponse.
400
+ // We also may be removing the optimistic layer without replacing it with
401
+ // anything, which would also be disallowed if the original stack was
402
+ // Base <- Opt <- NetworkResponse.
403
+ //
404
+ // Thus, instead, we will (1) replace the optimistic node's data with an empty object
405
+ // and attach the network response as a child.
406
+ const oldMergedData = optimisticNode.data;
407
+ optimisticNode.data = {};
408
+
409
+ let newMergedData = {};
410
+ let childNode = optimisticNode.childStoreLayer;
411
+ if (normalizeData != null) {
412
+ const networkResponseStoreLayer: NetworkResponseStoreLayer = {
413
+ kind: 'NetworkResponseStoreLayer',
414
+ data: {},
415
+ parentStoreLayer: optimisticNode,
416
+ childStoreLayer: null,
417
+ };
418
+ normalizeData(networkResponseStoreLayer);
419
+
420
+ if (childNode?.kind === 'NetworkResponseStoreLayer') {
421
+ // (2) if the optimistic layer's child was a network response, and we are
422
+ // replacing it with a network response, we must merge the replacement
423
+ // and the child.
424
+ mergeDataLayer(networkResponseStoreLayer.data, childNode.data);
425
+ mergeDataLayer(oldMergedData, childNode.data);
426
+ childNode = childNode.childStoreLayer;
427
+ }
428
+ newMergedData = structuredClone(networkResponseStoreLayer.data);
429
+ setChildOfNode(environment, networkResponseStoreLayer, childNode);
430
+ optimisticNode.childStoreLayer = networkResponseStoreLayer;
431
+ }
432
+
433
+ // (3) Re-execute all updates, accumulating all changed values into newMergedData.
434
+ // Since we have already written the network response into newMergedData, we
435
+ // can proceed from the child of the (potentially merged) network response layer.
436
+ //
437
+ // Note that it is important that reexecuteUpdatesAndMergeData is called here!
438
+ // That is because we created newMergedData from the network response layer's data,
439
+ // and later, we may merge that network response into the parent layer (if it is
440
+ // a base layer). That merged layer will contain many extraneous records (unless the
441
+ // base layer is empty).
442
+ //
443
+ // This would cause us to re-execute subscriptions unnecessarily, as these records
444
+ // do not represent changes between the optimistic and network response layers.
445
+ reexecuteUpdatesAndMergeData(childNode, oldMergedData, newMergedData);
446
+
447
+ // (4) Now, we can finally remove the optimistic layer, i.e. do
448
+ // optimistic.parent.child = optimistic.child.
449
+ // But the types don't line up, so we handle the cases differently, based on the
450
+ // parent layer type.
451
+ if (optimisticNode.parentStoreLayer.kind === 'BaseStoreLayer') {
452
+ // (4a) If the optimistic parent is the base layer, then we have a problem: base.child
453
+ // must be an optimistic layer or null. So, we merge the optimistic children into the
454
+ // base layer until we reach an optimistic layer.
455
+ mergeLayersWithDataIntoBaseLayer(
456
+ environment,
457
+ optimisticNode.childStoreLayer,
458
+ optimisticNode.parentStoreLayer,
459
+ );
460
+ } else if (
461
+ optimisticNode.parentStoreLayer.kind === 'NetworkResponseStoreLayer' &&
462
+ optimisticNode.childStoreLayer?.kind === 'NetworkResponseStoreLayer'
463
+ ) {
464
+ // (4b) if the parent is a network response layer, simply merge those. (We do not
465
+ // attempt to merge other layers, e.g. startUpdate layers, because there is some
466
+ // optimistic layer between this layer and the base, and the startUpdate will need
467
+ // to be recalculated if the optimistic layer is reverted.)
468
+ mergeDataLayer(
469
+ optimisticNode.parentStoreLayer.data,
470
+ optimisticNode.childStoreLayer.data,
471
+ );
472
+
473
+ setChildOfNode(
474
+ environment,
475
+ optimisticNode.parentStoreLayer,
476
+ optimisticNode.childStoreLayer.childStoreLayer,
477
+ );
478
+ } else {
479
+ // (4c) Otherwise, the parent is an optimistic or start update layer, and we can
480
+ // set optimistic.parent.child = optimistic.child.
481
+ setChildOfNode(
482
+ environment,
483
+ optimisticNode.parentStoreLayer,
484
+ optimisticNode.childStoreLayer,
485
+ );
486
+ }
487
+
488
+ // (5) finally, compare the oldMergedData and newMergedData objects, in order to extract
489
+ // the modified IDs, and re-execute subscriptions.
490
+ let encounteredIds: EncounteredIds = new Map();
491
+ compareData(oldMergedData, newMergedData, encounteredIds);
492
+ callSubscriptions(environment, encounteredIds);
493
+ }
494
+
495
+ export type StoreLayer =
496
+ | OptimisticStoreLayer
497
+ | NetworkResponseStoreLayer
498
+ | StartUpdateStoreLayer
499
+ | BaseStoreLayer;
500
+
501
+ export type StoreLayerWithData =
502
+ | BaseStoreLayer
503
+ | NetworkResponseStoreLayer
504
+ | OptimisticNetworkResponseStoreLayer;
505
+
506
+ function compareData(
507
+ oldData: StoreLayerData,
508
+ newData: StoreLayerData,
509
+ encounteredIds: EncounteredIds,
510
+ ): void {
511
+ const oldDataTypeNames = new Set(Object.keys(oldData));
512
+ const newDataTypeNames = new Set(Object.keys(newData));
513
+
514
+ for (const oldTypeName of oldDataTypeNames.difference(newDataTypeNames)) {
515
+ const set = insertEmptySetIfMissing(encounteredIds, oldTypeName);
516
+ for (const id in oldData[oldTypeName]) {
517
+ set.add(id);
518
+ }
519
+ }
520
+
521
+ for (const [typeName, newRecords] of Object.entries(newData)) {
522
+ if (newRecords == null) {
523
+ continue;
524
+ }
525
+ const oldRecords = oldData[typeName];
526
+ outer: for (const [id, newRecord] of Object.entries(newRecords)) {
527
+ if (newRecord == null) {
528
+ continue;
529
+ }
530
+ const oldRecord = oldRecords?.[id];
531
+
532
+ for (const [recordKey, newRecordValue] of Object.entries(newRecord)) {
533
+ // TODO: compare links, compare arrays
534
+ if (newRecordValue !== oldRecord?.[recordKey]) {
535
+ const set = insertEmptySetIfMissing(encounteredIds, typeName);
536
+ set.add(id);
537
+ continue outer;
538
+ }
539
+ }
540
+
541
+ encounteredIds.get(typeName)?.delete(id);
542
+ }
543
+ }
544
+ }