@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,31 @@
1
+ import { isPromise } from '@legendapp/state';
2
+ import { ObservableSyncGetParams, SyncedOptions, type ObservableSyncClass } from '@legendapp/state/sync';
3
+
4
+ export function syncObservableAdapter<T = {}>({ get, set }: SyncedOptions<T>): ObservableSyncClass {
5
+ const ret: ObservableSyncClass = {};
6
+
7
+ if (get) {
8
+ ret.get = (async (params: ObservableSyncGetParams<T>) => {
9
+ try {
10
+ let value = get(params as any);
11
+ if (isPromise(value)) {
12
+ value = await value;
13
+ }
14
+
15
+ params.onChange({
16
+ value,
17
+ lastSync: params.lastSync,
18
+ mode: params.mode!,
19
+ });
20
+ params.onGet();
21
+ // eslint-disable-next-line no-empty
22
+ } catch {}
23
+ }) as ObservableSyncClass['get'];
24
+ }
25
+
26
+ if (set) {
27
+ ret.set = set as unknown as ObservableSyncClass['set'];
28
+ }
29
+
30
+ return ret;
31
+ }
@@ -0,0 +1,188 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+ // @ts-ignore
3
+ import type { MMKVConfiguration } from 'react-native-mmkv';
4
+ // @ts-ignore
5
+ import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage';
6
+
7
+ import {
8
+ Change,
9
+ ClassConstructor,
10
+ GetMode,
11
+ LinkedOptions,
12
+ NodeValue,
13
+ SetParams,
14
+ UpdateFn,
15
+ UpdateFnParams,
16
+ } from '../observableInterfaces';
17
+ import { Observable, ObservableParam, ObservableState } from '../observableTypes';
18
+
19
+ export interface PersistOptions<T = any> {
20
+ name: string;
21
+ plugin?: ClassConstructor<ObservablePersistPlugin, T[]>;
22
+ retrySync?: boolean;
23
+ transform?: SyncTransform<T>;
24
+ readonly?: boolean;
25
+ mmkv?: MMKVConfiguration;
26
+ indexedDB?: {
27
+ prefixID?: string;
28
+ itemID?: string;
29
+ };
30
+ options?: any;
31
+ }
32
+
33
+ export interface SyncedGetParams {
34
+ value: any;
35
+ lastSync: number | undefined;
36
+ updateLastSync: (lastSync: number) => void;
37
+ mode: GetMode;
38
+ refresh: () => void;
39
+ }
40
+
41
+ export interface SyncedSetParams<T> extends SetParams<T> {
42
+ node: NodeValue;
43
+ valuePrevious: T;
44
+ update: UpdateFn;
45
+ refresh: () => void;
46
+ cancelRetry: () => void;
47
+ retryNum: number;
48
+ fromSubscribe: boolean | undefined;
49
+ }
50
+
51
+ export interface SyncedSubscribeParams {
52
+ node: NodeValue;
53
+ update: UpdateFn;
54
+ refresh: () => void;
55
+ }
56
+
57
+ export interface SyncedOptions<T = any> extends Omit<LinkedOptions<T>, 'get' | 'set'> {
58
+ get?: (params: SyncedGetParams) => Promise<T> | T;
59
+ set?: (params: SyncedSetParams<T>) => void | Promise<any>;
60
+ subscribe?: (params: SyncedSubscribeParams) => void;
61
+ retry?: RetryOptions;
62
+ persist?: PersistOptions<any>;
63
+ debounceSet?: number;
64
+ syncMode?: 'auto' | 'manual';
65
+ mode?: GetMode;
66
+ transform?: SyncTransform<T>;
67
+ // Not implemented yet
68
+ enableSync?: boolean;
69
+ onGetError?: (error: Error) => void;
70
+ onSetError?: (error: Error) => void;
71
+ log?: (message?: any, ...optionalParams: any[]) => void;
72
+ onBeforeSet?: () => void;
73
+ onAfterSet?: () => void;
74
+ allowSetIfGetError?: boolean;
75
+ }
76
+
77
+ export interface SyncedOptionsGlobal<T = any>
78
+ extends Omit<
79
+ SyncedOptions<T>,
80
+ 'get' | 'set' | 'persist' | 'initial' | 'waitForSet' | 'waitFor' | 'transform' | 'subscribe'
81
+ > {
82
+ persist?: ObservablePersistPluginOptions & Omit<PersistOptions, 'name' | 'transform' | 'options'>;
83
+ }
84
+
85
+ export interface ObservablePersistPluginOptions {
86
+ onGetError?: (error: Error) => void;
87
+ onSetError?: (error: Error) => void;
88
+ indexedDB?: {
89
+ databaseName: string;
90
+ version: number;
91
+ tableNames: string[];
92
+ };
93
+ asyncStorage?: {
94
+ AsyncStorage: AsyncStorageStatic;
95
+ preload?: boolean | string[];
96
+ };
97
+ }
98
+ export interface ObservablePersistPlugin {
99
+ initialize?(config: ObservablePersistPluginOptions): void | Promise<void>;
100
+ loadTable?(table: string, config: PersistOptions): Promise<any> | void;
101
+ getTable<T = any>(table: string, init: object, config: PersistOptions): T;
102
+ set(table: string, changes: Change[], config: PersistOptions): Promise<any> | void;
103
+ deleteTable(table: string, config: PersistOptions): Promise<any> | void;
104
+ getMetadata(table: string, config: PersistOptions): PersistMetadata;
105
+ setMetadata(table: string, metadata: PersistMetadata, config: PersistOptions): Promise<any> | void;
106
+ deleteMetadata(table: string, config: PersistOptions): Promise<any> | void;
107
+ }
108
+ export interface PersistMetadata {
109
+ id?: '__legend_metadata';
110
+ // modified ?: number;
111
+ lastSync?: number;
112
+ pending?: any;
113
+ }
114
+ export type SyncTransformMethod = 'get' | 'set';
115
+ export interface SyncTransform<TOrig = any, TSaved = TOrig> {
116
+ load?: (value: TSaved, method: SyncTransformMethod) => TOrig | Promise<TOrig>;
117
+ save?: (value: TOrig) => TSaved | Promise<TSaved>;
118
+ }
119
+ export interface ObservableSyncStateBase {
120
+ isPersistLoaded: boolean;
121
+ isPersistEnabled: boolean;
122
+ isSyncEnabled: boolean;
123
+ lastSync?: number;
124
+ syncCount?: number;
125
+ clearPersist: () => Promise<void>;
126
+ sync: () => Promise<void>;
127
+ getPendingChanges: () =>
128
+ | Record<
129
+ string,
130
+ {
131
+ p: any;
132
+ v?: any;
133
+ }
134
+ >
135
+ | undefined;
136
+ }
137
+ export type ObservableSyncState = ObservableState & ObservableSyncStateBase;
138
+
139
+ export interface ObservableSyncSetParams<T> {
140
+ syncState: Observable<ObservableSyncState>;
141
+ obs: ObservableParam<T>;
142
+ options: SyncedOptions<T>;
143
+ changes: Change[];
144
+ value: T;
145
+ valuePrevious: T;
146
+ }
147
+ export interface ObservableSyncGetParams<T> {
148
+ state: Observable<ObservableSyncState>;
149
+ obs: ObservableParam<T>;
150
+ options: SyncedOptions<T>;
151
+ dateModified?: number;
152
+ lastSync?: number;
153
+ mode?: GetMode;
154
+ onGet: () => void;
155
+ onError: (error: Error) => void;
156
+ onChange: (params: UpdateFnParams) => void | Promise<void>;
157
+ }
158
+ export type ObservableSyncRemoteGetFnParams<T> = Omit<ObservableSyncGetParams<T>, 'onGet'>;
159
+
160
+ export interface ObservableSyncClass {
161
+ get?<T>(params: ObservableSyncGetParams<T>): void;
162
+ set?<T>(
163
+ params: ObservableSyncSetParams<T>,
164
+ ): void | Promise<void | { changes?: object; dateModified?: number; lastSync?: number; pathStrs?: string[] }>;
165
+ }
166
+
167
+ export interface ObservableSyncFunctions<T = any> {
168
+ get?(params: ObservableSyncRemoteGetFnParams<T>): T | Promise<T>;
169
+ set?(
170
+ params: ObservableSyncSetParams<T>,
171
+ ): void | Promise<void | { changes?: object | undefined; dateModified?: number; lastSync?: number }>;
172
+ }
173
+
174
+ export interface RetryOptions {
175
+ infinite?: boolean;
176
+ times?: number;
177
+ delay?: number;
178
+ backoff?: 'constant' | 'exponential';
179
+ maxDelay?: number;
180
+ }
181
+
182
+ export interface SubscribeOptions {
183
+ node: NodeValue;
184
+ update: UpdateFn;
185
+ refresh: () => void;
186
+ }
187
+
188
+ export type Synced<T> = T;
@@ -0,0 +1,20 @@
1
+ import type { Synced, SyncedOptions } from './syncTypes';
2
+ import { internal } from '@legendapp/state';
3
+ import { enableActivateSyncedNode } from './activateSyncedNode';
4
+
5
+ const { symbolLinked } = internal;
6
+
7
+ export function synced<T>(params: SyncedOptions<T>): Synced<T> {
8
+ installPersistActivateNode();
9
+ return (() => ({
10
+ [symbolLinked]: { ...params, synced: true },
11
+ })) as any;
12
+ }
13
+
14
+ let didInstall = false;
15
+ function installPersistActivateNode() {
16
+ if (!didInstall) {
17
+ enableActivateSyncedNode();
18
+ didInstall = true;
19
+ }
20
+ }
@@ -0,0 +1,404 @@
1
+ import { internal, isArray, isNullOrUndefined, isNumber, isObject, isString } from '@legendapp/state';
2
+ import {
3
+ synced,
4
+ diffObjects,
5
+ SyncTransform,
6
+ SyncTransformMethod,
7
+ SyncedGetParams,
8
+ SyncedOptions,
9
+ SyncedSetParams,
10
+ } from '@legendapp/state/sync';
11
+
12
+ const { clone } = internal;
13
+
14
+ export type CrudAsOption = 'Map' | 'object' | 'first';
15
+
16
+ export type CrudResult<T> = T;
17
+
18
+ export interface SyncedCrudPropsSingle<TRemote, TLocal> {
19
+ get?: (params: SyncedGetParams) => Promise<CrudResult<TRemote | null>> | CrudResult<TRemote | null>;
20
+ initial?: InitialValue<TLocal, 'first'>;
21
+ as?: never | 'first';
22
+ }
23
+ export interface SyncedCrudPropsMany<TRemote, TLocal, TAsOption extends CrudAsOption> {
24
+ list?: (params: SyncedGetParams) => Promise<CrudResult<TRemote[] | null>> | CrudResult<TRemote[] | null>;
25
+ as?: TAsOption;
26
+ initial?: InitialValue<TLocal, TAsOption>;
27
+ }
28
+ export interface SyncedCrudPropsBase<TRemote extends { id: string | number }, TLocal = TRemote>
29
+ extends Omit<SyncedOptions<TLocal>, 'get' | 'set' | 'transform' | 'initial'> {
30
+ create?(input: TRemote, params: SyncedSetParams<TRemote>): Promise<CrudResult<TRemote> | null | undefined>;
31
+ update?(
32
+ input: Partial<TRemote>,
33
+ params: SyncedSetParams<TRemote>,
34
+ ): Promise<CrudResult<Partial<TRemote> | null | undefined>>;
35
+ delete?(input: TRemote, params: SyncedSetParams<TRemote>): Promise<CrudResult<any>>;
36
+ onSaved?(saved: TLocal, input: TRemote, isCreate: boolean): Partial<TLocal> | void;
37
+ transform?: SyncTransform<TLocal, TRemote>;
38
+ fieldUpdatedAt?: string;
39
+ fieldCreatedAt?: string;
40
+ updatePartial?: boolean;
41
+ changesSince?: 'all' | 'last-sync';
42
+ generateId?: () => string | number;
43
+ }
44
+
45
+ type InitialValue<T, TAsOption extends CrudAsOption> = TAsOption extends 'Map'
46
+ ? Map<string, T>
47
+ : TAsOption extends 'object'
48
+ ? Record<string, T>
49
+ : TAsOption extends 'first'
50
+ ? T
51
+ : T[];
52
+
53
+ export type SyncedCrudReturnType<TLocal, TAsOption extends CrudAsOption> = TAsOption extends 'Map'
54
+ ? Map<string, TLocal>
55
+ : TAsOption extends 'object'
56
+ ? Record<string, TLocal>
57
+ : TAsOption extends 'first'
58
+ ? TLocal
59
+ : TLocal[];
60
+
61
+ let _asOption: CrudAsOption;
62
+
63
+ function transformOut<T1, T2>(data: T1, transform: undefined | ((value: T1) => T2)) {
64
+ return transform ? transform(clone(data)) : data;
65
+ }
66
+
67
+ // TODO
68
+ export function createTransform<T extends Record<string, any>, T2 extends Record<string, any>>(
69
+ ...keys: (keyof T | { from: keyof T; to: keyof T2 })[]
70
+ ): SyncTransform<T2, T> {
71
+ return {
72
+ load: (value: T) => {
73
+ (keys as string[]).forEach((key) => {
74
+ const keyRemote = isObject(key) ? key.from : key;
75
+ const keyLocal = isObject(key) ? key.to : key;
76
+ const v = value[keyRemote];
77
+ if (!isNullOrUndefined(v)) {
78
+ value[keyLocal as keyof T] = isString(v) ? JSON.parse(v as string) : v;
79
+ }
80
+ if (keyLocal !== keyRemote) {
81
+ delete value[keyRemote];
82
+ }
83
+ });
84
+ return value as unknown as T2;
85
+ },
86
+ save: (value: T2) => {
87
+ (keys as string[]).forEach((key: string) => {
88
+ const keyRemote = isObject(key) ? key.from : key;
89
+ const keyLocal = isObject(key) ? key.to : key;
90
+ let v = (value as any)[keyLocal];
91
+ if (!isNullOrUndefined(v)) {
92
+ if (isArray(v)) {
93
+ v = v.filter((val) => !isNullOrUndefined(val));
94
+ }
95
+ value[keyRemote as keyof T2] =
96
+ isNumber(v) || isObject(v) || isArray(v) ? (JSON.stringify(v) as any) : v;
97
+ }
98
+ if (keyLocal !== keyRemote) {
99
+ delete value[keyLocal];
100
+ }
101
+ });
102
+ return value as unknown as T;
103
+ },
104
+ };
105
+ }
106
+
107
+ // TODO
108
+ export function combineTransforms<T, T2>(
109
+ transform1: SyncTransform<T2, T>,
110
+ ...transforms: Partial<SyncTransform<T2, T>>[]
111
+ ): SyncTransform<T2, T> {
112
+ return {
113
+ load: (value: T, method: SyncTransformMethod) => {
114
+ let inValue = transform1.load?.(value, method) as any;
115
+ transforms.forEach((transform) => {
116
+ if (transform.load) {
117
+ inValue = transform.load(inValue, method);
118
+ }
119
+ });
120
+ return inValue;
121
+ },
122
+ save: (value: T2) => {
123
+ let outValue = value as any;
124
+ transforms.forEach((transform) => {
125
+ if (transform.save) {
126
+ outValue = transform.save(outValue);
127
+ }
128
+ });
129
+ return transform1.save?.(outValue) ?? outValue;
130
+ },
131
+ };
132
+ }
133
+
134
+ function ensureId(obj: { id: string | number }, generateId: () => string | number) {
135
+ if (!obj.id) {
136
+ obj.id = generateId();
137
+ }
138
+ return obj.id;
139
+ }
140
+
141
+ export function syncedCrud<TRemote extends { id: string | number }, TLocal = TRemote>(
142
+ props: SyncedCrudPropsBase<TRemote, TLocal> & SyncedCrudPropsSingle<TRemote, TLocal>,
143
+ ): SyncedCrudReturnType<TLocal, 'first'>;
144
+ export function syncedCrud<
145
+ TRemote extends { id: string | number },
146
+ TLocal = TRemote,
147
+ TAsOption extends CrudAsOption = 'object',
148
+ >(
149
+ props: SyncedCrudPropsBase<TRemote, TLocal> & SyncedCrudPropsMany<TRemote, TLocal, TAsOption>,
150
+ ): SyncedCrudReturnType<TLocal, Exclude<TAsOption, 'first'>>;
151
+ export function syncedCrud<
152
+ TRemote extends { id: string | number },
153
+ TLocal = TRemote,
154
+ TAsOption extends CrudAsOption = 'object',
155
+ >(
156
+ props: SyncedCrudPropsBase<TRemote, TLocal> &
157
+ (SyncedCrudPropsSingle<TRemote, TLocal> & SyncedCrudPropsMany<TRemote, TLocal, TAsOption>),
158
+ ): SyncedCrudReturnType<TLocal, TAsOption> {
159
+ const {
160
+ get: getFn,
161
+ list: listFn,
162
+ create: createFn,
163
+ update: updateFn,
164
+ delete: deleteFn,
165
+ transform,
166
+ fieldCreatedAt,
167
+ fieldUpdatedAt,
168
+ updatePartial,
169
+ onSaved,
170
+ mode: modeParam,
171
+ changesSince,
172
+ generateId,
173
+ ...rest
174
+ } = props;
175
+
176
+ let asType = props.as as TAsOption;
177
+
178
+ if (!asType) {
179
+ asType = (getFn ? 'first' : _asOption || 'object') as CrudAsOption as TAsOption;
180
+ }
181
+
182
+ const asMap = asType === 'Map';
183
+
184
+ const get: undefined | ((params: SyncedGetParams) => Promise<TLocal>) =
185
+ getFn || listFn
186
+ ? async (getParams: SyncedGetParams) => {
187
+ const { updateLastSync, lastSync } = getParams;
188
+ if (listFn) {
189
+ if (changesSince === 'last-sync' && lastSync) {
190
+ getParams.mode = modeParam || (asType === 'first' ? 'set' : 'assign');
191
+ }
192
+
193
+ const data = (await listFn(getParams)) || [];
194
+ let newLastSync = 0;
195
+ for (let i = 0; i < data.length; i++) {
196
+ const updated =
197
+ (data[i] as any)[fieldUpdatedAt as any] || (data[i] as any)[fieldCreatedAt as any];
198
+ if (updated) {
199
+ newLastSync = Math.max(newLastSync, +new Date(updated));
200
+ }
201
+ }
202
+ if (newLastSync && newLastSync !== lastSync) {
203
+ updateLastSync(newLastSync);
204
+ }
205
+ let transformed = data as unknown as TLocal[];
206
+ if (transform?.load) {
207
+ transformed = await Promise.all(data.map((value) => transform.load!(value, 'get')));
208
+ }
209
+ if (asType === 'first') {
210
+ return transformed.length > 0 ? transformed[0] : null;
211
+ } else {
212
+ const out: Record<string, any> = asMap ? new Map() : {};
213
+ transformed.forEach((result: any) => {
214
+ const value = result.__deleted ? internal.symbolDelete : result;
215
+ asMap ? (out as Map<any, any>).set(result.id, value) : (out[result.id] = value);
216
+ });
217
+ return out;
218
+ }
219
+ } else if (getFn) {
220
+ const data = await getFn(getParams);
221
+
222
+ let transformed = data as unknown as TLocal;
223
+ if (data) {
224
+ const newLastSync =
225
+ (data as any)[fieldUpdatedAt as any] || (data as any)[fieldCreatedAt as any];
226
+ if (newLastSync && newLastSync !== lastSync) {
227
+ updateLastSync(newLastSync);
228
+ }
229
+ if (transform?.load) {
230
+ transformed = await transform.load(data, 'get');
231
+ }
232
+ }
233
+
234
+ return transformed as any;
235
+ }
236
+ }
237
+ : undefined;
238
+
239
+ const set =
240
+ createFn || updateFn || deleteFn
241
+ ? async (params: SyncedSetParams<any> & { retryAsCreate?: boolean }) => {
242
+ const { value, changes, update, retryAsCreate, valuePrevious } = params;
243
+ const creates = new Map<string, TLocal>();
244
+ const updates = new Map<string, object>();
245
+ const deletes = new Map<string, object>();
246
+
247
+ changes.forEach(({ path, prevAtPath, valueAtPath }) => {
248
+ if (asType === 'first') {
249
+ if (value) {
250
+ let id = value?.id;
251
+ const isCreate = fieldCreatedAt ? !value[fieldCreatedAt!] : !prevAtPath;
252
+ if (!id && generateId) {
253
+ id = ensureId(value, generateId);
254
+ }
255
+ if (id) {
256
+ if (isCreate || retryAsCreate) {
257
+ creates.set(id, value);
258
+ } else if (path.length === 0) {
259
+ if (valueAtPath) {
260
+ updates.set(id, valueAtPath);
261
+ } else if (prevAtPath) {
262
+ deletes.set(prevAtPath?.id, prevAtPath);
263
+ }
264
+ } else {
265
+ const key = path[0];
266
+ updates.set(id, Object.assign(updates.get(id) || { id }, { [key]: value[key] }));
267
+ }
268
+ } else {
269
+ console.error('[legend-state]: added item without an id');
270
+ }
271
+ } else if (path.length === 0) {
272
+ const id = prevAtPath?.id;
273
+ if (id) {
274
+ deletes.set(id, prevAtPath);
275
+ }
276
+ }
277
+ } else {
278
+ let itemsChanged: any[] | undefined = undefined;
279
+ let isCreateGuess: boolean;
280
+ if (path.length === 0) {
281
+ isCreateGuess =
282
+ !(fieldCreatedAt || fieldUpdatedAt) &&
283
+ !(
284
+ (asMap
285
+ ? Array.from((valueAtPath as Map<any, any>).values())
286
+ : isArray(valueAtPath)
287
+ ? valueAtPath
288
+ : Object.values(valueAtPath)
289
+ )?.length > 0
290
+ );
291
+ itemsChanged = asMap
292
+ ? Array.from((valueAtPath as Map<any, any>).values())
293
+ : isArray(valueAtPath)
294
+ ? valueAtPath
295
+ : Object.values(valueAtPath);
296
+ } else {
297
+ const itemKey = path[0];
298
+ const itemValue = asMap ? value.get(itemKey) : value[itemKey];
299
+ isCreateGuess = !(fieldCreatedAt || fieldUpdatedAt) && path.length === 1 && !prevAtPath;
300
+ if (!itemValue) {
301
+ if (path.length === 1 && prevAtPath) {
302
+ if (deleteFn) {
303
+ deletes.set(itemKey, prevAtPath);
304
+ } else {
305
+ console.log('[legend-state] missing delete function');
306
+ }
307
+ }
308
+ } else {
309
+ itemsChanged = [itemValue];
310
+ }
311
+ }
312
+ itemsChanged?.forEach((item) => {
313
+ const isCreate = fieldCreatedAt
314
+ ? !item[fieldCreatedAt!]
315
+ : fieldUpdatedAt
316
+ ? !item[fieldUpdatedAt]
317
+ : isCreateGuess;
318
+ if (isCreate) {
319
+ if (generateId) {
320
+ ensureId(item, generateId);
321
+ }
322
+ if (!item.id) {
323
+ console.error('[legend-state]: added item without an id');
324
+ }
325
+ if (createFn) {
326
+ creates.set(item.id, item);
327
+ } else {
328
+ console.log('[legend-state] missing create function');
329
+ }
330
+ } else {
331
+ if (updateFn) {
332
+ updates.set(item.id, item);
333
+ } else {
334
+ console.log('[legend-state] missing update function');
335
+ }
336
+ }
337
+ });
338
+ }
339
+ });
340
+
341
+ const saveResult = async (
342
+ itemKey: string,
343
+ input: TRemote,
344
+ data: CrudResult<TRemote>,
345
+ isCreate: boolean,
346
+ ) => {
347
+ if (data && onSaved) {
348
+ const dataLoaded: TLocal = (
349
+ transform?.load ? transform.load(data as any, 'set') : data
350
+ ) as any;
351
+
352
+ const savedOut = onSaved(dataLoaded, input, isCreate);
353
+
354
+ if (savedOut) {
355
+ const createdAt = fieldCreatedAt ? savedOut[fieldCreatedAt as keyof TLocal] : undefined;
356
+ const updatedAt = fieldUpdatedAt ? savedOut[fieldUpdatedAt as keyof TLocal] : undefined;
357
+
358
+ const value =
359
+ itemKey !== 'undefined' && asType !== 'first' ? { [itemKey]: savedOut } : savedOut;
360
+ update({
361
+ value,
362
+ lastSync:
363
+ updatedAt || createdAt ? +new Date(updatedAt || (createdAt as any)) : undefined,
364
+ mode: 'merge',
365
+ });
366
+ }
367
+ }
368
+ };
369
+
370
+ return Promise.all([
371
+ ...Array.from(creates).map(([itemKey, itemValue]) => {
372
+ const createObj = transformOut(itemValue, transform?.save) as TRemote;
373
+ return createFn!(createObj, params).then((result) =>
374
+ saveResult(itemKey, createObj, result as any, true),
375
+ );
376
+ }),
377
+ ...Array.from(updates).map(([itemKey, itemValue]) => {
378
+ const toSave = updatePartial
379
+ ? Object.assign(
380
+ diffObjects(asType === 'first' ? valuePrevious : valuePrevious[itemKey], itemValue),
381
+ { id: (itemValue as any).id },
382
+ )
383
+ : itemValue;
384
+ const changed = transformOut(toSave as TLocal, transform?.save) as TRemote;
385
+
386
+ if (Object.keys(changed).length > 0) {
387
+ return updateFn!(changed, params).then((result) =>
388
+ saveResult(itemKey, changed, result as any, false),
389
+ );
390
+ }
391
+ }),
392
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
393
+ ...Array.from(deletes).map(([_, itemValue]) => deleteFn!(itemValue as TRemote, params)),
394
+ ]);
395
+ }
396
+ : undefined;
397
+
398
+ return synced<any>({
399
+ set,
400
+ get,
401
+ mode: modeParam,
402
+ ...rest,
403
+ });
404
+ }