@legendapp/state 2.2.0-next.8 → 2.2.0-next.80

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 (321) hide show
  1. package/README.md +4 -2
  2. package/babel.js.map +1 -1
  3. package/config/enable$get.d.ts +8 -0
  4. package/config/enable$get.js +24 -0
  5. package/config/enable$get.js.map +1 -0
  6. package/config/enable$get.mjs +21 -0
  7. package/config/enable$get.mjs.map +1 -0
  8. package/config/enableReactComponents.js.map +1 -1
  9. package/config/enableReactComponents.mjs.map +1 -1
  10. package/config/enableReactNativeComponents.js.map +1 -1
  11. package/config/enableReactNativeComponents.mjs.map +1 -1
  12. package/config/enableReactTracking.d.ts +0 -9
  13. package/config/enableReactTracking.js +34 -32
  14. package/config/enableReactTracking.js.map +1 -1
  15. package/config/enableReactTracking.mjs +35 -33
  16. package/config/enableReactTracking.mjs.map +1 -1
  17. package/config/enableReactUse.d.ts +1 -1
  18. package/config/enableReactUse.js +9 -1
  19. package/config/enableReactUse.js.map +1 -1
  20. package/config/enableReactUse.mjs +9 -1
  21. package/config/enableReactUse.mjs.map +1 -1
  22. package/config/enable_peek.d.ts +8 -0
  23. package/config/{enableDirectPeek.js → enable_peek.js} +6 -3
  24. package/config/enable_peek.js.map +1 -0
  25. package/config/{enableDirectPeek.mjs → enable_peek.mjs} +5 -3
  26. package/config/enable_peek.mjs.map +1 -0
  27. package/helpers/fetch.d.ts +4 -3
  28. package/helpers/fetch.js.map +1 -1
  29. package/helpers/fetch.mjs.map +1 -1
  30. package/helpers/pageHash.js.map +1 -1
  31. package/helpers/pageHash.mjs.map +1 -1
  32. package/helpers/pageHashParams.js.map +1 -1
  33. package/helpers/pageHashParams.mjs.map +1 -1
  34. package/helpers/time.d.ts +2 -2
  35. package/helpers/time.js.map +1 -1
  36. package/helpers/time.mjs.map +1 -1
  37. package/history.js +2 -2
  38. package/history.js.map +1 -1
  39. package/history.mjs +3 -3
  40. package/history.mjs.map +1 -1
  41. package/index.d.ts +29 -9
  42. package/index.js +964 -671
  43. package/index.js.map +1 -1
  44. package/index.mjs +959 -668
  45. package/index.mjs.map +1 -1
  46. package/package.json +37 -25
  47. package/persist-plugins/async-storage.d.ts +4 -3
  48. package/persist-plugins/async-storage.js +8 -7
  49. package/persist-plugins/async-storage.js.map +1 -1
  50. package/persist-plugins/async-storage.mjs +9 -8
  51. package/persist-plugins/async-storage.mjs.map +1 -1
  52. package/persist-plugins/fetch.d.ts +1 -1
  53. package/persist-plugins/fetch.js.map +1 -1
  54. package/persist-plugins/fetch.mjs.map +1 -1
  55. package/persist-plugins/firebase.d.ts +2 -2
  56. package/persist-plugins/firebase.js +12 -11
  57. package/persist-plugins/firebase.js.map +1 -1
  58. package/persist-plugins/firebase.mjs +13 -12
  59. package/persist-plugins/firebase.mjs.map +1 -1
  60. package/persist-plugins/indexeddb.d.ts +11 -10
  61. package/persist-plugins/indexeddb.js +2 -2
  62. package/persist-plugins/indexeddb.js.map +1 -1
  63. package/persist-plugins/indexeddb.mjs +2 -2
  64. package/persist-plugins/indexeddb.mjs.map +1 -1
  65. package/persist-plugins/local-storage.d.ts +4 -4
  66. package/persist-plugins/local-storage.js +19 -7
  67. package/persist-plugins/local-storage.js.map +1 -1
  68. package/persist-plugins/local-storage.mjs +20 -9
  69. package/persist-plugins/local-storage.mjs.map +1 -1
  70. package/persist-plugins/mmkv.d.ts +9 -8
  71. package/persist-plugins/mmkv.js +5 -4
  72. package/persist-plugins/mmkv.js.map +1 -1
  73. package/persist-plugins/mmkv.mjs +6 -5
  74. package/persist-plugins/mmkv.mjs.map +1 -1
  75. package/persist-plugins/query.d.ts +1 -1
  76. package/persist-plugins/query.js.map +1 -1
  77. package/persist-plugins/query.mjs.map +1 -1
  78. package/persist.d.ts +2 -14
  79. package/persist.js +1270 -269
  80. package/persist.js.map +1 -1
  81. package/persist.mjs +1270 -270
  82. package/persist.mjs.map +1 -1
  83. package/react-hooks/createObservableHook.js +1 -1
  84. package/react-hooks/createObservableHook.js.map +1 -1
  85. package/react-hooks/createObservableHook.mjs +1 -1
  86. package/react-hooks/createObservableHook.mjs.map +1 -1
  87. package/react-hooks/useFetch.d.ts +4 -3
  88. package/react-hooks/useFetch.js.map +1 -1
  89. package/react-hooks/useFetch.mjs.map +1 -1
  90. package/react-hooks/useHover.js.map +1 -1
  91. package/react-hooks/useHover.mjs.map +1 -1
  92. package/react-hooks/useMeasure.js.map +1 -1
  93. package/react-hooks/useMeasure.mjs.map +1 -1
  94. package/react-hooks/useObservableNextRouter.js.map +1 -1
  95. package/react-hooks/useObservableNextRouter.mjs.map +1 -1
  96. package/react-hooks/useObservableQuery.js.map +1 -1
  97. package/react-hooks/useObservableQuery.mjs.map +1 -1
  98. package/react-hooks/usePersistedObservable.d.ts +6 -3
  99. package/react-hooks/usePersistedObservable.js +5 -2
  100. package/react-hooks/usePersistedObservable.js.map +1 -1
  101. package/react-hooks/usePersistedObservable.mjs +5 -2
  102. package/react-hooks/usePersistedObservable.mjs.map +1 -1
  103. package/react.js +73 -93
  104. package/react.js.map +1 -1
  105. package/react.mjs +73 -93
  106. package/react.mjs.map +1 -1
  107. package/src/ObservableObject.ts +1217 -0
  108. package/src/ObservablePrimitive.ts +62 -0
  109. package/src/babel/index.ts +70 -0
  110. package/src/batching.ts +378 -0
  111. package/src/computed.ts +18 -0
  112. package/src/config/enable$get.ts +30 -0
  113. package/src/config/enableReactComponents.ts +26 -0
  114. package/src/config/enableReactNativeComponents.ts +102 -0
  115. package/src/config/enableReactTracking.ts +62 -0
  116. package/src/config/enableReactUse.ts +32 -0
  117. package/src/config/enable_peek.ts +31 -0
  118. package/src/config.ts +47 -0
  119. package/src/createObservable.ts +46 -0
  120. package/src/event.ts +26 -0
  121. package/src/globals.ts +234 -0
  122. package/src/helpers/fetch.ts +26 -0
  123. package/src/helpers/pageHash.ts +41 -0
  124. package/src/helpers/pageHashParams.ts +55 -0
  125. package/src/helpers/time.ts +30 -0
  126. package/src/helpers.ts +224 -0
  127. package/src/history/trackHistory.ts +29 -0
  128. package/src/history/undoRedo.ts +111 -0
  129. package/src/is.ts +63 -0
  130. package/src/linked.ts +6 -0
  131. package/src/observable.ts +32 -0
  132. package/src/observableInterfaces.ts +148 -0
  133. package/src/observableTypes.ts +226 -0
  134. package/src/observe.ts +89 -0
  135. package/src/onChange.ts +136 -0
  136. package/src/persist/configureObservablePersistence.ts +7 -0
  137. package/src/persist/fieldTransformer.ts +149 -0
  138. package/src/persist/observablePersistRemoteFunctionsAdapter.ts +39 -0
  139. package/src/persist/persistObservable.ts +1031 -0
  140. package/src/persist-plugins/async-storage.ts +102 -0
  141. package/src/persist-plugins/fetch.ts +34 -0
  142. package/src/persist-plugins/firebase.ts +1052 -0
  143. package/src/persist-plugins/indexeddb.ts +432 -0
  144. package/src/persist-plugins/local-storage.ts +91 -0
  145. package/src/persist-plugins/mmkv.ts +91 -0
  146. package/src/persist-plugins/query.ts +129 -0
  147. package/src/proxy.ts +28 -0
  148. package/src/react/Computed.tsx +7 -0
  149. package/src/react/For.tsx +116 -0
  150. package/src/react/Memo.tsx +4 -0
  151. package/src/react/Reactive.tsx +53 -0
  152. package/src/react/Show.tsx +33 -0
  153. package/src/react/Switch.tsx +43 -0
  154. package/src/react/react-globals.ts +3 -0
  155. package/src/react/{reactInterfaces.d.ts → reactInterfaces.ts} +15 -7
  156. package/src/react/reactive-observer.tsx +210 -0
  157. package/src/react/useComputed.ts +36 -0
  158. package/src/react/useEffectOnce.ts +41 -0
  159. package/src/react/useIsMounted.ts +16 -0
  160. package/src/react/useMount.ts +15 -0
  161. package/src/react/useObservable.ts +24 -0
  162. package/src/react/useObservableReducer.ts +52 -0
  163. package/src/react/useObservableState.ts +30 -0
  164. package/src/react/useObserve.ts +54 -0
  165. package/src/react/useObserveEffect.ts +40 -0
  166. package/src/react/usePauseProvider.tsx +13 -0
  167. package/src/react/useSelector.ts +167 -0
  168. package/src/react/useUnmount.ts +8 -0
  169. package/src/react/useWhen.ts +9 -0
  170. package/src/react-hooks/createObservableHook.ts +53 -0
  171. package/src/react-hooks/useFetch.ts +16 -0
  172. package/src/react-hooks/useHover.ts +40 -0
  173. package/src/react-hooks/useMeasure.ts +48 -0
  174. package/src/react-hooks/useObservableNextRouter.ts +137 -0
  175. package/src/react-hooks/useObservableQuery.ts +205 -0
  176. package/src/react-hooks/usePersistedObservable.ts +25 -0
  177. package/src/retry.ts +71 -0
  178. package/src/setupTracking.ts +26 -0
  179. package/src/sync/activateSyncedNode.ts +128 -0
  180. package/src/sync/configureObservableSync.ts +7 -0
  181. package/src/sync/persistTypes.ts +226 -0
  182. package/src/sync/syncHelpers.ts +56 -0
  183. package/src/sync/syncObservable.ts +1040 -0
  184. package/src/sync/syncObservableAdapter.ts +31 -0
  185. package/src/sync/syncTypes.ts +188 -0
  186. package/src/sync/synced.ts +20 -0
  187. package/src/sync-plugins/crud.ts +404 -0
  188. package/src/sync-plugins/fetch.ts +72 -0
  189. package/src/sync-plugins/keel.ts +452 -0
  190. package/src/sync-plugins/supabase.ts +261 -0
  191. package/src/trace/traceHelpers.ts +11 -0
  192. package/src/trace/useTraceListeners.ts +34 -0
  193. package/src/trace/useTraceUpdates.ts +24 -0
  194. package/src/trace/useVerifyNotTracking.ts +33 -0
  195. package/src/trace/useVerifyOneRender.ts +10 -0
  196. package/src/trackSelector.ts +52 -0
  197. package/src/tracking.ts +43 -0
  198. package/src/types/babel.d.ts +12 -0
  199. package/src/when.ts +75 -0
  200. package/sync-plugins/crud.d.ts +41 -0
  201. package/sync-plugins/crud.js +290 -0
  202. package/sync-plugins/crud.js.map +1 -0
  203. package/sync-plugins/crud.mjs +286 -0
  204. package/sync-plugins/crud.mjs.map +1 -0
  205. package/sync-plugins/fetch.d.ts +13 -0
  206. package/sync-plugins/fetch.js +46 -0
  207. package/sync-plugins/fetch.js.map +1 -0
  208. package/sync-plugins/fetch.mjs +44 -0
  209. package/sync-plugins/fetch.mjs.map +1 -0
  210. package/sync-plugins/keel.d.ts +91 -0
  211. package/sync-plugins/keel.js +277 -0
  212. package/sync-plugins/keel.js.map +1 -0
  213. package/sync-plugins/keel.mjs +273 -0
  214. package/sync-plugins/keel.mjs.map +1 -0
  215. package/sync-plugins/supabase.d.ts +36 -0
  216. package/sync-plugins/supabase.js +152 -0
  217. package/sync-plugins/supabase.js.map +1 -0
  218. package/sync-plugins/supabase.mjs +149 -0
  219. package/sync-plugins/supabase.mjs.map +1 -0
  220. package/sync.d.ts +11 -0
  221. package/sync.js +976 -0
  222. package/sync.js.map +1 -0
  223. package/sync.mjs +966 -0
  224. package/sync.mjs.map +1 -0
  225. package/trace.js +13 -10
  226. package/trace.js.map +1 -1
  227. package/trace.mjs +11 -8
  228. package/trace.mjs.map +1 -1
  229. package/types/babel.d.ts +3 -3
  230. package/config/enableDirectAccess.d.ts +0 -7
  231. package/config/enableDirectAccess.js +0 -25
  232. package/config/enableDirectAccess.js.map +0 -1
  233. package/config/enableDirectAccess.mjs +0 -23
  234. package/config/enableDirectAccess.mjs.map +0 -1
  235. package/config/enableDirectPeek.d.ts +0 -7
  236. package/config/enableDirectPeek.js.map +0 -1
  237. package/config/enableDirectPeek.mjs.map +0 -1
  238. package/config/enableReactDirectRender.d.ts +0 -2
  239. package/config/enableReactDirectRender.js +0 -78
  240. package/config/enableReactDirectRender.js.map +0 -1
  241. package/config/enableReactDirectRender.mjs +0 -75
  242. package/config/enableReactDirectRender.mjs.map +0 -1
  243. package/src/ObservableObject.d.ts +0 -14
  244. package/src/ObservablePrimitive.d.ts +0 -7
  245. package/src/babel/index.d.ts +0 -17
  246. package/src/batching.d.ts +0 -6
  247. package/src/computed.d.ts +0 -4
  248. package/src/config/enableDirectAccess.d.ts +0 -7
  249. package/src/config/enableDirectPeek.d.ts +0 -7
  250. package/src/config/enableReactComponents.d.ts +0 -7
  251. package/src/config/enableReactDirectRender.d.ts +0 -2
  252. package/src/config/enableReactNativeComponents.d.ts +0 -20
  253. package/src/config/enableReactTracking.d.ts +0 -15
  254. package/src/config/enableReactUse.d.ts +0 -7
  255. package/src/config.d.ts +0 -8
  256. package/src/createObservable.d.ts +0 -2
  257. package/src/event.d.ts +0 -2
  258. package/src/globals.d.ts +0 -32
  259. package/src/helpers/fetch.d.ts +0 -6
  260. package/src/helpers/pageHash.d.ts +0 -7
  261. package/src/helpers/pageHashParams.d.ts +0 -7
  262. package/src/helpers/time.d.ts +0 -3
  263. package/src/helpers.d.ts +0 -13
  264. package/src/history/trackHistory.d.ts +0 -4
  265. package/src/is.d.ts +0 -10
  266. package/src/observable.d.ts +0 -16
  267. package/src/observableInterfaces.d.ts +0 -458
  268. package/src/observe.d.ts +0 -6
  269. package/src/onChange.d.ts +0 -7
  270. package/src/persist/configureObservablePersistence.d.ts +0 -3
  271. package/src/persist/fieldTransformer.d.ts +0 -8
  272. package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +0 -2
  273. package/src/persist/persistActivateNode.d.ts +0 -1
  274. package/src/persist/persistHelpers.d.ts +0 -1
  275. package/src/persist/persistObservable.d.ts +0 -25
  276. package/src/persist-plugins/async-storage.d.ts +0 -14
  277. package/src/persist-plugins/fetch.d.ts +0 -10
  278. package/src/persist-plugins/firebase.d.ts +0 -51
  279. package/src/persist-plugins/indexeddb.d.ts +0 -25
  280. package/src/persist-plugins/local-storage.d.ts +0 -21
  281. package/src/persist-plugins/mmkv.d.ts +0 -14
  282. package/src/persist-plugins/query.d.ts +0 -18
  283. package/src/proxy.d.ts +0 -5
  284. package/src/react/Computed.d.ts +0 -5
  285. package/src/react/For.d.ts +0 -15
  286. package/src/react/Memo.d.ts +0 -3
  287. package/src/react/Reactive.d.ts +0 -9
  288. package/src/react/Show.d.ts +0 -18
  289. package/src/react/Switch.d.ts +0 -14
  290. package/src/react/react-globals.d.ts +0 -3
  291. package/src/react/reactive-observer.d.ts +0 -14
  292. package/src/react/useComputed.d.ts +0 -5
  293. package/src/react/useEffectOnce.d.ts +0 -1
  294. package/src/react/useIsMounted.d.ts +0 -2
  295. package/src/react/useMount.d.ts +0 -2
  296. package/src/react/useObservable.d.ts +0 -9
  297. package/src/react/useObservableReducer.d.ts +0 -7
  298. package/src/react/useObservableState.d.ts +0 -2
  299. package/src/react/useObserve.d.ts +0 -4
  300. package/src/react/useObserveEffect.d.ts +0 -4
  301. package/src/react/usePauseProvider.d.ts +0 -8
  302. package/src/react/useSelector.d.ts +0 -3
  303. package/src/react/useUnmount.d.ts +0 -2
  304. package/src/react/useWhen.d.ts +0 -3
  305. package/src/react-hooks/createObservableHook.d.ts +0 -2
  306. package/src/react-hooks/useFetch.d.ts +0 -6
  307. package/src/react-hooks/useHover.d.ts +0 -3
  308. package/src/react-hooks/useMeasure.d.ts +0 -6
  309. package/src/react-hooks/useObservableNextRouter.d.ts +0 -33
  310. package/src/react-hooks/useObservableQuery.d.ts +0 -6
  311. package/src/react-hooks/usePersistedObservable.d.ts +0 -11
  312. package/src/retry.d.ts +0 -9
  313. package/src/setupTracking.d.ts +0 -2
  314. package/src/trace/traceHelpers.d.ts +0 -2
  315. package/src/trace/useTraceListeners.d.ts +0 -1
  316. package/src/trace/useTraceUpdates.d.ts +0 -1
  317. package/src/trace/useVerifyNotTracking.d.ts +0 -1
  318. package/src/trace/useVerifyOneRender.d.ts +0 -1
  319. package/src/trackSelector.d.ts +0 -7
  320. package/src/tracking.d.ts +0 -13
  321. package/src/when.d.ts +0 -3
@@ -0,0 +1,72 @@
1
+ import { Selector, computeSelector } from '@legendapp/state';
2
+ import { synced, SyncTransform, Synced, SyncedOptions, SyncedSetParams } from '@legendapp/state/sync';
3
+
4
+ export interface SyncedFetchProps<TRemote, TLocal> extends Omit<SyncedOptions, 'get' | 'set' | 'transform'> {
5
+ get: Selector<string>;
6
+ set?: Selector<string>;
7
+ getInit?: RequestInit;
8
+ setInit?: RequestInit;
9
+ transform?: SyncTransform<TLocal, TRemote>;
10
+ valueType?: 'arrayBuffer' | 'blob' | 'formData' | 'json' | 'text';
11
+ onSavedValueType?: 'arrayBuffer' | 'blob' | 'formData' | 'json' | 'text';
12
+ onSaved?(saved: TLocal, input: TRemote): Partial<TLocal> | void;
13
+ }
14
+
15
+ export function syncedFetch<TRemote, TLocal = TRemote>(props: SyncedFetchProps<TRemote, TLocal>): Synced<TLocal> {
16
+ const {
17
+ get: getParam,
18
+ set: setParam,
19
+ getInit,
20
+ setInit,
21
+ valueType,
22
+ onSaved,
23
+ onSavedValueType,
24
+ transform,
25
+ ...rest
26
+ } = props;
27
+ const get = async () => {
28
+ const url = computeSelector(getParam);
29
+ const response = await fetch(url, getInit);
30
+
31
+ if (!response.ok) {
32
+ throw new Error(response.statusText);
33
+ }
34
+
35
+ let value = await response[valueType || 'json']();
36
+
37
+ if (transform?.load) {
38
+ value = transform?.load(value, 'get');
39
+ }
40
+
41
+ return value;
42
+ };
43
+
44
+ let set: ((params: SyncedSetParams<TRemote>) => void | Promise<any>) | undefined = undefined;
45
+ if (setParam) {
46
+ set = async ({ value, update }: SyncedSetParams<any>) => {
47
+ const url = computeSelector(setParam);
48
+
49
+ const response = await fetch(
50
+ url,
51
+ Object.assign({ method: 'POST' }, setInit, { body: JSON.stringify(value) }),
52
+ );
53
+ if (!response.ok) {
54
+ throw new Error(response.statusText);
55
+ }
56
+ if (onSaved) {
57
+ const responseValue = await response[onSavedValueType || valueType || 'json']();
58
+ const transformed = transform?.load ? await transform.load(responseValue, 'set') : responseValue;
59
+ const valueSave = onSaved(transformed, value);
60
+ update({
61
+ value: valueSave,
62
+ });
63
+ }
64
+ };
65
+ }
66
+
67
+ return synced({
68
+ ...rest,
69
+ get,
70
+ set,
71
+ });
72
+ }
@@ -0,0 +1,452 @@
1
+ import { computeSelector, observable, when, internal } from '@legendapp/state';
2
+ import {
3
+ SyncedOptions,
4
+ removeNullUndefined,
5
+ type SyncedGetParams,
6
+ type SyncedSetParams,
7
+ type SyncedSubscribeParams,
8
+ } from '@legendapp/state/sync';
9
+ import {
10
+ CrudAsOption,
11
+ CrudResult,
12
+ SyncedCrudPropsBase,
13
+ SyncedCrudPropsMany,
14
+ SyncedCrudPropsSingle,
15
+ SyncedCrudReturnType,
16
+ syncedCrud,
17
+ } from '@legendapp/state/sync-plugins/crud';
18
+ const { clone } = internal;
19
+
20
+ // Keel types
21
+ export interface KeelObjectBase {
22
+ id: string;
23
+ createdAt: Date;
24
+ updatedAt: Date;
25
+ }
26
+ export type KeelKey = 'createdAt' | 'updatedAt';
27
+ export const KeelKeys: KeelKey[] = ['createdAt', 'updatedAt'];
28
+ export type OmitKeelBuiltins<T, T2 extends string = ''> = Omit<T, KeelKey | T2>;
29
+ type APIError = { type: string; message: string; requestId?: string };
30
+
31
+ type APIResult<T> = Result<T, APIError>;
32
+
33
+ type Data<T> = {
34
+ data: T;
35
+ error?: never;
36
+ };
37
+
38
+ type Err<U> = {
39
+ data?: never;
40
+ error: U;
41
+ };
42
+
43
+ type Result<T, U> = NonNullable<Data<T> | Err<U>>;
44
+
45
+ // Keel plugin types
46
+
47
+ interface GetGetParams {
48
+ refresh: () => void;
49
+ }
50
+
51
+ interface ListGetParams {
52
+ where: { updatedAt?: { after: Date } };
53
+ refresh?: () => void;
54
+ after?: string;
55
+ first?: number;
56
+ }
57
+
58
+ export interface KeelRealtimePlugin {
59
+ subscribe: (realtimeKey: string, refresh: () => void) => void;
60
+ setLatestChange: (realtimeKey: string, time: Date) => void;
61
+ }
62
+
63
+ export interface SyncedKeelConfiguration
64
+ extends Omit<
65
+ SyncedCrudPropsBase<any>,
66
+ | keyof SyncedOptions
67
+ | 'create'
68
+ | 'update'
69
+ | 'delete'
70
+ | 'onSaved'
71
+ | 'transform'
72
+ | 'updatePartial'
73
+ | 'subscribe'
74
+ | 'fieldCreatedAt'
75
+ | 'fieldUpdatedAt'
76
+ > {
77
+ client: {
78
+ auth: { refresh: () => Promise<boolean>; isAuthenticated: () => Promise<boolean> };
79
+ api: { queries: Record<string, (i: any) => Promise<any>> };
80
+ };
81
+ realtimePlugin?: KeelRealtimePlugin;
82
+ as?: Exclude<CrudAsOption, 'first'>;
83
+ enabled?: boolean;
84
+ onError?: (params: APIResult<any>['error']) => void;
85
+ }
86
+
87
+ interface PageInfo {
88
+ count: number;
89
+ endCursor: string;
90
+ hasNextPage: boolean;
91
+ startCursor: string;
92
+ totalCount: number;
93
+ }
94
+
95
+ interface SyncedKeelPropsMany<TRemote, TLocal, AOption extends CrudAsOption>
96
+ extends Omit<SyncedCrudPropsMany<TRemote, TLocal, AOption>, 'list'> {
97
+ list?: (params: ListGetParams) => Promise<CrudResult<APIResult<{ results: TRemote[]; pageInfo: any }>>>;
98
+ first?: number;
99
+ get?: never;
100
+ }
101
+
102
+ interface SyncedKeelPropsSingle<TRemote, TLocal> extends Omit<SyncedCrudPropsSingle<TRemote, TLocal>, 'get'> {
103
+ get?: (params: GetGetParams) => Promise<APIResult<TRemote>>;
104
+
105
+ first?: never;
106
+ list?: never;
107
+ as?: never;
108
+ }
109
+
110
+ interface SyncedKeelPropsBase<TRemote extends { id: string }, TLocal = TRemote>
111
+ extends Omit<SyncedCrudPropsBase<TRemote, TLocal>, 'create' | 'update' | 'delete'> {
112
+ create?: (i: NoInfer<Partial<TRemote>>) => Promise<APIResult<NoInfer<TRemote>>>;
113
+ update?: (params: { where: any; values?: Partial<TRemote> }) => Promise<APIResult<TRemote>>;
114
+ delete?: (params: { id: string }) => Promise<APIResult<string>>;
115
+ }
116
+
117
+ const keelConfig: SyncedKeelConfiguration = {} as SyncedKeelConfiguration;
118
+ const modifiedClients = new WeakSet<Record<string, any>>();
119
+ const isEnabled$ = observable(true);
120
+
121
+ async function ensureAuthToken() {
122
+ await when(isEnabled$.get());
123
+ let isAuthed = await keelConfig.client.auth.isAuthenticated();
124
+ if (!isAuthed) {
125
+ isAuthed = await keelConfig.client.auth.refresh();
126
+ }
127
+
128
+ return isAuthed;
129
+ }
130
+
131
+ async function handleApiError(error: APIError, retry?: () => any) {
132
+ if (error.type === 'unauthorized' || error.type === 'forbidden') {
133
+ console.warn('Keel token expired, refreshing...');
134
+ await ensureAuthToken();
135
+ // Retry
136
+ retry?.();
137
+ }
138
+ }
139
+
140
+ function convertObjectToCreate<TRemote, TLocal>(item: TRemote) {
141
+ const cloned = clone(item);
142
+ Object.keys(cloned).forEach((key) => {
143
+ if (key.endsWith('Id')) {
144
+ if (cloned[key]) {
145
+ cloned[key.slice(0, -2)] = { id: cloned[key] };
146
+ }
147
+ delete cloned[key];
148
+ }
149
+ });
150
+ delete cloned.createdAt;
151
+ delete cloned.updatedAt;
152
+ return cloned as unknown as TLocal;
153
+ }
154
+
155
+ export function configureSyncedKeel(config: SyncedKeelConfiguration) {
156
+ const { enabled, realtimePlugin, ...rest } = config;
157
+ Object.assign(keelConfig, removeNullUndefined(rest));
158
+
159
+ if (enabled !== undefined) {
160
+ isEnabled$.set(enabled);
161
+ }
162
+ const { client } = keelConfig;
163
+
164
+ if (realtimePlugin) {
165
+ keelConfig.realtimePlugin = realtimePlugin;
166
+ if (client && !modifiedClients.has(client)) {
167
+ modifiedClients.add(client);
168
+ const queries = client.api.queries;
169
+ Object.keys(queries).forEach((key) => {
170
+ const oldFn = queries[key];
171
+ queries[key] = (i) => {
172
+ const subscribe =
173
+ key.startsWith('list') &&
174
+ i.where &&
175
+ (({ refresh }: SyncedSubscribeParams) => {
176
+ const realtimeChild = Object.values(i.where)
177
+ .filter((value) => value && typeof value !== 'object')
178
+ .join('/');
179
+
180
+ if (realtimeChild) {
181
+ const realtimeKey = `${key}/${realtimeChild}`;
182
+
183
+ realtimePlugin.subscribe(realtimeKey, refresh);
184
+ return realtimeKey;
185
+ }
186
+ });
187
+ return oldFn(i).then((ret) => {
188
+ if (subscribe) {
189
+ ret.subscribe = subscribe;
190
+ }
191
+ return ret;
192
+ });
193
+ };
194
+ });
195
+ }
196
+ }
197
+ }
198
+
199
+ const NumPerPage = 200;
200
+ async function getAllPages<TRemote>(
201
+ listFn: (params: ListGetParams) => Promise<
202
+ APIResult<{
203
+ results: TRemote[];
204
+ pageInfo: any;
205
+ }>
206
+ >,
207
+ params: ListGetParams,
208
+ ): Promise<{ results: TRemote[]; subscribe: (params: { refresh: () => void }) => string }> {
209
+ const allData: TRemote[] = [];
210
+ let pageInfo: PageInfo | undefined = undefined;
211
+ let subscribe_;
212
+
213
+ const { first: firstParam } = params;
214
+
215
+ do {
216
+ const first = firstParam ? Math.min(firstParam - allData.length, NumPerPage) : NumPerPage;
217
+ if (first < 1) {
218
+ break;
219
+ }
220
+ const pageEndCursor = pageInfo?.endCursor;
221
+ const paramsWithCursor: ListGetParams = pageEndCursor
222
+ ? { first, ...params, after: pageEndCursor }
223
+ : { first, ...params };
224
+ pageInfo = undefined;
225
+ const ret = await listFn(paramsWithCursor);
226
+
227
+ if (ret) {
228
+ // @ts-expect-error TODOKEEL
229
+ const { data, error, subscribe } = ret;
230
+
231
+ if (subscribe) {
232
+ subscribe_ = subscribe;
233
+ }
234
+
235
+ if (error) {
236
+ await handleApiError(error);
237
+ throw new Error(error.message);
238
+ } else if (data) {
239
+ pageInfo = data.pageInfo as PageInfo;
240
+ allData.push(...data.results);
241
+ }
242
+ }
243
+ } while (pageInfo?.hasNextPage);
244
+
245
+ return { results: allData, subscribe: subscribe_ };
246
+ }
247
+
248
+ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote>(
249
+ props: SyncedKeelPropsBase<TRemote, TLocal> & SyncedKeelPropsSingle<TRemote, TLocal>,
250
+ ): SyncedCrudReturnType<TLocal, 'first'>;
251
+ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOption extends CrudAsOption = 'object'>(
252
+ props: SyncedKeelPropsBase<TRemote, TLocal> & SyncedKeelPropsMany<TRemote, TLocal, TOption>,
253
+ ): SyncedCrudReturnType<TLocal, Exclude<TOption, 'first'>>;
254
+ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOption extends CrudAsOption = 'object'>(
255
+ props: SyncedKeelPropsBase<TRemote, TLocal> &
256
+ (SyncedKeelPropsSingle<TRemote, TLocal> | SyncedKeelPropsMany<TRemote, TLocal, TOption>),
257
+ ): SyncedCrudReturnType<TLocal, TOption> {
258
+ const {
259
+ get: getParam,
260
+ list: listParam,
261
+ create: createParam,
262
+ update: updateParam,
263
+ delete: deleteParam,
264
+ first,
265
+ waitFor,
266
+ waitForSet,
267
+ generateId: generateIdParam,
268
+ ...rest
269
+ } = props;
270
+
271
+ const { changesSince } = props;
272
+
273
+ let asType = props.as as TOption;
274
+
275
+ if (!asType) {
276
+ asType = (getParam ? 'first' : keelConfig.as || undefined) as TOption;
277
+ }
278
+
279
+ const generateId = generateIdParam || keelConfig.generateId;
280
+
281
+ const realtimePlugin = keelConfig.realtimePlugin;
282
+ let realtimeKeyList: string | undefined = undefined;
283
+ let realtimeKeyGet: string | undefined = undefined;
284
+
285
+ const fieldCreatedAt: KeelKey = 'createdAt';
286
+ const fieldUpdatedAt: KeelKey = 'updatedAt';
287
+
288
+ const list = listParam
289
+ ? async (listParams: SyncedGetParams) => {
290
+ const { lastSync, refresh } = listParams;
291
+ const queryBySync = !!lastSync && changesSince === 'last-sync';
292
+ // If this is one of the customized functions for use with realtime then we need to pass
293
+ // the refresh function to it
294
+ const isRawRequest = (listParam || getParam).toString().includes('rawRequest');
295
+ // If querying with lastSync pass it to the "where" parameters
296
+ const where = queryBySync ? { updatedAt: { after: new Date(+new Date(lastSync) + 1) } } : {};
297
+ const params: ListGetParams = isRawRequest ? { where, first } : { where, refresh, first };
298
+
299
+ // TODO: Error?
300
+ const { results, subscribe } = await getAllPages(listParam, params);
301
+ if (!realtimeKeyList) {
302
+ realtimeKeyList = subscribe?.({ refresh });
303
+ }
304
+
305
+ return results;
306
+ }
307
+ : undefined;
308
+
309
+ const get = getParam
310
+ ? async (getParams: SyncedGetParams) => {
311
+ const { refresh } = getParams;
312
+ // @ts-expect-error TODOKEEL
313
+ const { data, error, subscribe } = await getParam({ refresh });
314
+ if (!realtimeKeyGet) {
315
+ realtimeKeyGet = subscribe?.({ refresh });
316
+ }
317
+
318
+ if (error) {
319
+ throw new Error(error.message);
320
+ } else {
321
+ return data as TRemote;
322
+ }
323
+ }
324
+ : undefined;
325
+
326
+ const onSaved = (data: TLocal, input: TRemote, isCreate: boolean): Partial<TLocal> | void => {
327
+ if (data) {
328
+ const savedOut: Partial<TLocal> = {};
329
+ if (isCreate) {
330
+ // Update with any fields that were undefined when creating
331
+ Object.keys(data).forEach((key) => {
332
+ if (input[key as keyof TRemote] === undefined) {
333
+ savedOut[key as keyof TLocal] = data[key as keyof TLocal];
334
+ }
335
+ });
336
+ } else {
337
+ // Update with any fields ending in createdAt or updatedAt
338
+ Object.keys(data).forEach((key) => {
339
+ const k = key as keyof TLocal;
340
+ const keyLower = key.toLowerCase();
341
+ if ((keyLower.endsWith('createdat') || keyLower.endsWith('updatedat')) && data[k] instanceof Date) {
342
+ savedOut[k] = data[k];
343
+ }
344
+ });
345
+ }
346
+
347
+ const updatedAt = data[fieldUpdatedAt as keyof TLocal] as Date;
348
+
349
+ if (updatedAt && realtimePlugin) {
350
+ if (realtimeKeyGet) {
351
+ realtimePlugin.setLatestChange(realtimeKeyGet, updatedAt);
352
+ }
353
+ if (realtimeKeyList) {
354
+ realtimePlugin.setLatestChange(realtimeKeyList, updatedAt);
355
+ }
356
+ }
357
+
358
+ return savedOut;
359
+ }
360
+ };
361
+
362
+ const handleSetError = async (error: APIError, params: SyncedSetParams<TRemote>, isCreate: boolean) => {
363
+ const { retryNum, cancelRetry, update } = params;
364
+
365
+ if (
366
+ isCreate &&
367
+ (error.message as string)?.includes('for the unique') &&
368
+ (error.message as string)?.includes('must be unique')
369
+ ) {
370
+ if (__DEV__) {
371
+ console.log('Creating duplicate data already saved, just ignore.');
372
+ }
373
+ // This has already been saved but didn't update pending changes, so just update with {} to clear the pending state
374
+ update({
375
+ value: {},
376
+ mode: 'assign',
377
+ });
378
+ } else if (error.type === 'bad_request') {
379
+ keelConfig.onError?.(error);
380
+
381
+ if (retryNum > 4) {
382
+ cancelRetry();
383
+ }
384
+
385
+ throw new Error(error.message);
386
+ } else {
387
+ await handleApiError(error);
388
+
389
+ throw new Error(error.message);
390
+ }
391
+ };
392
+
393
+ const create = createParam
394
+ ? async (input: TRemote, params: SyncedSetParams<TRemote>) => {
395
+ const { data, error } = await createParam(convertObjectToCreate(input));
396
+
397
+ if (error) {
398
+ handleSetError(error, params, true);
399
+ }
400
+
401
+ return data;
402
+ }
403
+ : undefined;
404
+
405
+ const update = updateParam
406
+ ? async (input: TRemote, params: SyncedSetParams<TRemote>) => {
407
+ const id = input.id;
408
+ const values = input as unknown as Partial<KeelObjectBase>;
409
+ delete values.id;
410
+ delete values.createdAt;
411
+ delete values.updatedAt;
412
+
413
+ const { data, error } = await updateParam({ where: { id }, values: input });
414
+
415
+ if (error) {
416
+ handleSetError(error, params, false);
417
+ }
418
+
419
+ return data;
420
+ }
421
+ : undefined;
422
+ const deleteFn = deleteParam
423
+ ? async (input: TRemote & { id: string }, params: SyncedSetParams<TRemote>) => {
424
+ const { data, error } = await deleteParam({ id: input.id });
425
+
426
+ if (error) {
427
+ handleSetError(error, params, false);
428
+ }
429
+
430
+ return data;
431
+ }
432
+ : undefined;
433
+
434
+ return syncedCrud<TRemote, TLocal, TOption>({
435
+ ...rest,
436
+ as: asType,
437
+ list,
438
+ create,
439
+ update,
440
+ delete: deleteFn,
441
+ waitFor: () => isEnabled$.get() && (waitFor ? computeSelector(waitFor) : true),
442
+ waitForSet: () => isEnabled$.get() && (waitForSet ? computeSelector(waitForSet) : true),
443
+ onSaved,
444
+ fieldCreatedAt,
445
+ fieldUpdatedAt,
446
+ changesSince,
447
+ updatePartial: true,
448
+ generateId,
449
+ // @ts-expect-error This errors because of the get/list union type
450
+ get: get as any,
451
+ }) as SyncedCrudReturnType<TLocal, TOption>;
452
+ }