@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,261 @@
1
+ import {
2
+ Observable,
3
+ computeSelector,
4
+ getNodeValue,
5
+ mergeIntoObservable,
6
+ observable,
7
+ symbolDelete,
8
+ } from '@legendapp/state';
9
+ import {
10
+ SyncedOptions,
11
+ SyncedOptionsGlobal,
12
+ removeNullUndefined,
13
+ type SyncedGetParams,
14
+ type SyncedSubscribeParams,
15
+ } from '@legendapp/state/sync';
16
+ import {
17
+ CrudAsOption,
18
+ SyncedCrudPropsBase,
19
+ SyncedCrudPropsMany,
20
+ SyncedCrudReturnType,
21
+ syncedCrud,
22
+ } from '@legendapp/state/sync-plugins/crud';
23
+ import type { PostgrestFilterBuilder, PostgrestQueryBuilder } from '@supabase/postgrest-js';
24
+ import type { SupabaseClient } from '@supabase/supabase-js';
25
+
26
+ // Unused types but maybe useful in the future so keeping them for now
27
+ // type DatabaseOf<Client extends SupabaseClient> = Client extends SupabaseClient<infer TDB> ? TDB : never;
28
+ // type SchemaNameOf<Client extends SupabaseClient> = Client extends SupabaseClient<infer _, infer SchemaName>
29
+ // ? SchemaName
30
+ // : never;
31
+
32
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
+ type SchemaOf<Client extends SupabaseClient> = Client extends SupabaseClient<infer _, infer __, infer Schema>
34
+ ? Schema
35
+ : never;
36
+ type TableOf<Client extends SupabaseClient> = SchemaOf<Client>['Tables'];
37
+ type CollectionOf<Client extends SupabaseClient> = keyof TableOf<Client>;
38
+ type RowOf<Client extends SupabaseClient, Collection extends CollectionOf<Client>> = TableOf<Client>[Collection]['Row'];
39
+
40
+ export type SyncedSupabaseConfig<T extends { id: string }> = Omit<
41
+ SyncedCrudPropsBase<T>,
42
+ 'create' | 'update' | 'delete' | 'onSaved' | 'transform' | 'updatePartial' | 'subscribe'
43
+ >;
44
+
45
+ export interface SyncedSupabaseGlobalConfig
46
+ extends Omit<SyncedSupabaseConfig<{ id: string }>, 'persist' | keyof SyncedOptions> {
47
+ persist?: SyncedOptionsGlobal;
48
+ enabled?: Observable<boolean>;
49
+ as?: Exclude<CrudAsOption, 'first'>;
50
+ }
51
+
52
+ interface SyncedSupabaseProps<
53
+ Client extends SupabaseClient,
54
+ Collection extends CollectionOf<Client>,
55
+ TOption extends CrudAsOption = 'object',
56
+ > extends SyncedSupabaseConfig<RowOf<Client, Collection>>,
57
+ SyncedCrudPropsMany<RowOf<Client, Collection>, RowOf<Client, Collection>, TOption> {
58
+ supabase: Client;
59
+ collection: Collection;
60
+ select?: (
61
+ query: PostgrestQueryBuilder<SchemaOf<Client>, TableOf<Client>[Collection], Collection>,
62
+ ) => PostgrestFilterBuilder<
63
+ SchemaOf<Client>,
64
+ RowOf<Client, Collection>,
65
+ RowOf<Client, Collection>[],
66
+ Collection,
67
+ []
68
+ >;
69
+ filter?: (
70
+ select: PostgrestFilterBuilder<
71
+ SchemaOf<Client>,
72
+ RowOf<Client, Collection>,
73
+ RowOf<Client, Collection>[],
74
+ Collection,
75
+ []
76
+ >,
77
+ params: SyncedGetParams,
78
+ ) => PostgrestFilterBuilder<
79
+ SchemaOf<Client>,
80
+ RowOf<Client, Collection>,
81
+ RowOf<Client, Collection>[],
82
+ Collection,
83
+ []
84
+ >;
85
+ actions?: ('create' | 'read' | 'update' | 'delete')[];
86
+ realtime?: { schema?: string; filter?: string };
87
+ }
88
+
89
+ let channelNum = 1;
90
+ const supabaseConfig: SyncedSupabaseGlobalConfig = {};
91
+ const isEnabled$ = observable(true);
92
+
93
+ export function configureSyncedSupabase(config: SyncedSupabaseGlobalConfig) {
94
+ const { enabled, ...rest } = config;
95
+ if (enabled !== undefined) {
96
+ isEnabled$.set(enabled);
97
+ }
98
+ Object.assign(supabaseConfig, removeNullUndefined(rest));
99
+ }
100
+
101
+ export function syncedSupabase<
102
+ Client extends SupabaseClient,
103
+ Collection extends CollectionOf<Client> & string,
104
+ AsOption extends CrudAsOption = 'object',
105
+ >(props: SyncedSupabaseProps<Client, Collection, AsOption>): SyncedCrudReturnType<RowOf<Client, Collection>, AsOption> {
106
+ mergeIntoObservable(props, supabaseConfig);
107
+ const {
108
+ supabase: client,
109
+ collection,
110
+ select: selectFn,
111
+ filter,
112
+ actions,
113
+ fieldCreatedAt,
114
+ fieldUpdatedAt,
115
+ realtime,
116
+ changesSince,
117
+ waitFor,
118
+ waitForSet,
119
+ generateId: generateIdParam,
120
+ ...rest
121
+ } = props;
122
+
123
+ const generateId = generateIdParam || supabaseConfig.generateId;
124
+
125
+ const list =
126
+ !actions || actions.includes('read')
127
+ ? async (params: SyncedGetParams) => {
128
+ const { lastSync } = params;
129
+ const from = client.from(collection);
130
+ let select = selectFn ? selectFn(from) : from.select();
131
+ if (changesSince === 'last-sync') {
132
+ select = select.neq('deleted', true);
133
+ if (lastSync) {
134
+ const date = new Date(lastSync).toISOString();
135
+ select = select.or(
136
+ [
137
+ fieldCreatedAt && `${fieldCreatedAt}.gt.${date}`,
138
+ fieldUpdatedAt && `${fieldUpdatedAt}.gt.${date}`,
139
+ ].join(','),
140
+ );
141
+ }
142
+ }
143
+ if (filter) {
144
+ select = filter(select, params);
145
+ }
146
+ const { data, error } = await select;
147
+ if (error) {
148
+ throw new Error(error?.message);
149
+ }
150
+ return (data! || []) as RowOf<Client, Collection>[];
151
+ }
152
+ : undefined;
153
+
154
+ const upsert = async (input: RowOf<Client, Collection>) => {
155
+ const res = await client.from(collection).upsert(input).select();
156
+ const { data, error } = res;
157
+ if (data) {
158
+ const created = data[0];
159
+ return created;
160
+ } else {
161
+ throw new Error(error?.message);
162
+ }
163
+ };
164
+ const create = !actions || actions.includes('create') ? upsert : undefined;
165
+ const update = !actions || actions.includes('update') ? upsert : undefined;
166
+ const deleteFn =
167
+ !actions || actions.includes('delete')
168
+ ? async (input: RowOf<Client, Collection>) => {
169
+ const id = input.id;
170
+ const from = client.from(collection);
171
+ const res = await (changesSince === 'last-sync' ? from.update({ deleted: true }) : from.delete())
172
+ .eq('id', id)
173
+ .select();
174
+ const { data, error } = res;
175
+ if (data) {
176
+ const created = data[0];
177
+ return created;
178
+ } else {
179
+ throw new Error(error?.message);
180
+ }
181
+ }
182
+ : undefined;
183
+ const subscribe = realtime
184
+ ? ({ node, update }: SyncedSubscribeParams) => {
185
+ const { filter, schema } = realtime;
186
+ const channel = client
187
+ .channel(`LS_${node.key || ''}${channelNum++}`)
188
+ .on(
189
+ 'postgres_changes',
190
+ {
191
+ event: '*',
192
+ table: collection,
193
+ schema: schema || 'public',
194
+ filter: filter || undefined,
195
+ },
196
+ (payload) => {
197
+ const { eventType, new: value, old } = payload;
198
+ if (eventType === 'INSERT' || eventType === 'UPDATE') {
199
+ const cur = getNodeValue(node)?.[value.id];
200
+ const curDateStr =
201
+ cur &&
202
+ ((fieldUpdatedAt && cur[fieldUpdatedAt]) ||
203
+ fieldCreatedAt ||
204
+ cur[fieldCreatedAt as any]);
205
+ const valueDateStr =
206
+ (fieldUpdatedAt && value[fieldUpdatedAt]) ||
207
+ (fieldCreatedAt && value[fieldCreatedAt]);
208
+ const valueDate = +new Date(valueDateStr);
209
+ // Check if new or newer than last seen locally
210
+ if (valueDateStr && (!curDateStr || valueDate > +new Date(curDateStr))) {
211
+ // Update local with the new value
212
+ update({
213
+ value: { [value.id]: value },
214
+ lastSync: valueDate,
215
+ mode: 'merge',
216
+ });
217
+ }
218
+ } else if (eventType === 'DELETE') {
219
+ const { id } = old;
220
+ update({
221
+ value: { [id]: symbolDelete },
222
+ });
223
+ }
224
+ },
225
+ )
226
+ .subscribe();
227
+
228
+ return channel.unsubscribe;
229
+ }
230
+ : undefined;
231
+
232
+ return syncedCrud<RowOf<Client, Collection>, RowOf<Client, Collection>, AsOption>({
233
+ ...rest,
234
+ list,
235
+ create,
236
+ update,
237
+ delete: deleteFn,
238
+ onSaved: (saved) => {
239
+ // Update the local timestamps with server response
240
+ if (fieldCreatedAt || fieldUpdatedAt) {
241
+ const ret: any = {
242
+ id: saved.id,
243
+ };
244
+ if (fieldCreatedAt) {
245
+ ret[fieldCreatedAt] = fieldCreatedAt;
246
+ }
247
+ if (fieldUpdatedAt) {
248
+ ret[fieldUpdatedAt] = fieldUpdatedAt;
249
+ }
250
+ return ret;
251
+ }
252
+ },
253
+ subscribe,
254
+ fieldCreatedAt,
255
+ fieldUpdatedAt,
256
+ updatePartial: true,
257
+ generateId,
258
+ waitFor: () => isEnabled$.get() && (waitFor ? computeSelector(waitFor) : true),
259
+ waitForSet: () => isEnabled$.get() && (waitForSet ? computeSelector(waitForSet) : true),
260
+ });
261
+ }
@@ -0,0 +1,11 @@
1
+ import type { NodeValue } from '@legendapp/state';
2
+
3
+ export function getNodePath(node: NodeValue) {
4
+ const arr: (string | number)[] = [];
5
+ let n = node;
6
+ while (n?.key !== undefined) {
7
+ arr.splice(0, 0, n.key);
8
+ n = n.parent;
9
+ }
10
+ return arr.join('.');
11
+ }
@@ -0,0 +1,34 @@
1
+ import { NodeValue, internal, TrackingNode } from '@legendapp/state';
2
+ import { getNodePath } from './traceHelpers';
3
+ const { optimized, tracking } = internal;
4
+
5
+ export function useTraceListeners(this: any, name?: string) {
6
+ if (process.env.NODE_ENV === 'development' && tracking.current) {
7
+ tracking.current.traceListeners = traceNodes.bind(this, name);
8
+ }
9
+ }
10
+
11
+ function traceNodes(name: string | undefined, nodes: Map<NodeValue, TrackingNode>) {
12
+ if (process.env.NODE_ENV === 'development' && tracking.current) {
13
+ tracking.current.traceListeners = undefined;
14
+ const arr: string[] = [];
15
+ if (nodes) {
16
+ for (const tracked of nodes.values()) {
17
+ const { node, track } = tracked;
18
+ const shallow = track === true;
19
+ const isOptimized = track === optimized;
20
+ arr.push(
21
+ `${arr.length + 1}: ${getNodePath(node)}${shallow ? ' (shallow)' : ''}${
22
+ isOptimized ? ' (optimized)' : ''
23
+ }`,
24
+ );
25
+ }
26
+ }
27
+
28
+ console.log(
29
+ `[legend-state] ${name ? name + ' ' : ''}tracking ${arr.length} observable${
30
+ arr.length !== 1 ? 's' : ''
31
+ }:\n${arr.join('\n')}`,
32
+ );
33
+ }
34
+ }
@@ -0,0 +1,24 @@
1
+ import { ListenerParams, internal } from '@legendapp/state';
2
+ const { tracking } = internal;
3
+
4
+ export function useTraceUpdates(name?: string) {
5
+ if (process.env.NODE_ENV === 'development' && tracking.current) {
6
+ tracking.current.traceUpdates = replaceUpdateFn.bind(undefined, name);
7
+ }
8
+ }
9
+
10
+ function replaceUpdateFn(name: string | undefined, updateFn: Function) {
11
+ return onChange.bind(undefined, name, updateFn);
12
+ }
13
+
14
+ function onChange(name: string | undefined, updateFn: Function, params: ListenerParams<any>) {
15
+ const { changes } = params;
16
+ if (process.env.NODE_ENV === 'development') {
17
+ changes.forEach(({ path, valueAtPath, prevAtPath }) => {
18
+ console.log(`[legend-state] Rendering ${name ? name + ' ' : ''}because "${path}" changed:
19
+ from: ${JSON.stringify(prevAtPath)}
20
+ to: ${JSON.stringify(valueAtPath)}`);
21
+ });
22
+ return updateFn();
23
+ }
24
+ }
@@ -0,0 +1,33 @@
1
+ import { internal, NodeValue, TrackingNode } from '@legendapp/state';
2
+ import { getNodePath } from './traceHelpers';
3
+ const { optimized, tracking } = internal;
4
+
5
+ export function useVerifyNotTracking(this: any, name?: string) {
6
+ if (process.env.NODE_ENV === 'development') {
7
+ tracking.current!.traceListeners = traceNodes.bind(this, name);
8
+ }
9
+ }
10
+
11
+ function traceNodes(name: string | undefined, nodes: Map<NodeValue, TrackingNode>) {
12
+ if (process.env.NODE_ENV === 'development') {
13
+ tracking.current!.traceListeners = undefined;
14
+ const arr: string[] = [];
15
+ if (nodes) {
16
+ for (const tracked of nodes.values()) {
17
+ const { node, track } = tracked;
18
+ const shallow = track === true;
19
+ const isOptimized = track === optimized;
20
+ arr.push(
21
+ `${arr.length + 1}: ${getNodePath(node)}${shallow ? ' (shallow)' : ''}${
22
+ isOptimized ? ' (optimized)' : ''
23
+ }`,
24
+ );
25
+ }
26
+ console.error(
27
+ `[legend-state] ${name ? name + ' ' : ''}tracking ${arr.length} observable${
28
+ arr.length !== 1 ? 's' : ''
29
+ } when it should not be:\n${arr.join('\n')}`,
30
+ );
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,10 @@
1
+ import { useRef } from 'react';
2
+
3
+ export function useVerifyOneRender(name?: string) {
4
+ if (process.env.NODE_ENV === 'development') {
5
+ const numRenders = ++useRef(0).current;
6
+ if (numRenders > 1) {
7
+ console.error(`[legend-state] ${name ? name + ' ' : ''}Component rendered more than once`);
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,52 @@
1
+ import { computeSelector } from './helpers';
2
+ import type { ListenerParams, ObserveEvent, Selector } from './observableInterfaces';
3
+ import type { ObserveOptions } from './observe';
4
+ import { setupTracking } from './setupTracking';
5
+ import { beginTracking, endTracking, tracking } from './tracking';
6
+
7
+ export function trackSelector<T>(
8
+ selector: Selector<T>,
9
+ update: (params: ListenerParams) => void,
10
+ observeEvent?: ObserveEvent<T>,
11
+ observeOptions?: ObserveOptions,
12
+ createResubscribe?: boolean,
13
+ ) {
14
+ let dispose: undefined | (() => void);
15
+ let resubscribe: (() => () => void) | undefined;
16
+ let updateFn = update;
17
+
18
+ beginTracking();
19
+ const value = selector ? computeSelector(selector, observeEvent, observeOptions?.fromComputed) : selector;
20
+ const tracker = tracking.current;
21
+ const nodes = tracker!.nodes;
22
+ endTracking();
23
+
24
+ if ((process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') && tracker && nodes) {
25
+ tracker.traceListeners?.(nodes);
26
+ if (tracker.traceUpdates) {
27
+ updateFn = tracker.traceUpdates(update) as () => void;
28
+ }
29
+ // Clear tracing so it doesn't leak to other components
30
+ tracker.traceListeners = undefined;
31
+ tracker.traceUpdates = undefined;
32
+ }
33
+
34
+ if (!observeEvent?.cancel) {
35
+ // Do tracing if it was requested
36
+
37
+ // useSyncExternalStore doesn't subscribe until after the component mount.
38
+ // We want to subscribe immediately so we don't miss any updates
39
+ dispose = setupTracking(nodes, updateFn, false, observeOptions?.immediate);
40
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
41
+ resubscribe = createResubscribe
42
+ ? () => {
43
+ dispose?.();
44
+ dispose = setupTracking(nodes, updateFn);
45
+ return dispose;
46
+ }
47
+ : undefined;
48
+ }
49
+ }
50
+
51
+ return { nodes, value, dispose, resubscribe };
52
+ }
@@ -0,0 +1,43 @@
1
+ import type { NodeValue, TrackingState, TrackingType } from './observableInterfaces';
2
+
3
+ let trackCount = 0;
4
+ const trackingQueue: (TrackingState | undefined)[] = [];
5
+
6
+ export const tracking = {
7
+ current: undefined as TrackingState | undefined,
8
+ };
9
+
10
+ export function beginTracking() {
11
+ // Keep a copy of the previous tracking context so it can be restored
12
+ // when this context is complete
13
+ trackingQueue.push(tracking.current);
14
+ trackCount++;
15
+ tracking.current = {};
16
+ }
17
+ export function endTracking() {
18
+ // Restore the previous tracking context
19
+ trackCount--;
20
+ if (trackCount < 0) {
21
+ trackCount = 0;
22
+ }
23
+ tracking.current = trackingQueue.pop();
24
+ }
25
+
26
+ export function updateTracking(node: NodeValue, track?: TrackingType) {
27
+ if (trackCount) {
28
+ const tracker = tracking.current;
29
+ if (tracker) {
30
+ if (!tracker.nodes) {
31
+ tracker.nodes = new Map();
32
+ }
33
+
34
+ const existing = tracker.nodes.get(node);
35
+ if (existing) {
36
+ existing.track = existing.track || track;
37
+ existing.num++;
38
+ } else {
39
+ tracker.nodes.set(node, { node, track, num: 1 });
40
+ }
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ import type { ObservableParam } from '@legendapp/state';
2
+ import { ReactNode } from 'react';
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ import type { Computed, Memo } from '@legendapp/state/react';
5
+ declare module '@legendapp/state/react' {
6
+ export declare const Computed: (props: {
7
+ children: ObservableParam | (() => ReactNode) | ReactNode;
8
+ }) => React.ReactElement;
9
+ export declare const Memo: (props: {
10
+ children: ObservableParam | (() => ReactNode) | ReactNode;
11
+ }) => React.ReactElement;
12
+ }
package/src/when.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { computeSelector, isObservableValueReady } from './helpers';
2
+ import { isPromise } from './is';
3
+ import type { ObserveEvent, Selector } from './observableInterfaces';
4
+ import { observe } from './observe';
5
+
6
+ function _when<T, T2>(predicate: Selector<T>, effect?: (value: T) => T2, checkReady?: boolean): any {
7
+ // If predicate is a regular Promise skip all the observable stuff
8
+ if (isPromise<T>(predicate)) {
9
+ return effect ? predicate.then(effect) : (predicate as any);
10
+ }
11
+
12
+ let value: T | undefined;
13
+ let effectValue: T2 | undefined;
14
+
15
+ // Create a wrapping fn that calls the effect if predicate returns true
16
+ function run(e: ObserveEvent<T>) {
17
+ const ret = computeSelector(predicate);
18
+
19
+ if (isPromise(ret)) {
20
+ value = ret as any;
21
+ // We want value to be the Promise but return undefined
22
+ // so it doesn't run the effect with the Promise as the value
23
+ return undefined;
24
+ } else if (!isPromise(ret) && (checkReady ? isObservableValueReady(ret) : ret)) {
25
+ value = ret;
26
+
27
+ // Set cancel so that observe does not track anymore
28
+ e.cancel = true;
29
+ }
30
+
31
+ return value;
32
+ }
33
+ function doEffect() {
34
+ // If value is truthy then run the effect
35
+ effectValue = effect?.(value!);
36
+ }
37
+ // Run in an observe
38
+ observe(run, doEffect);
39
+
40
+ // If first run resulted in a truthy value just return it.
41
+ // It will have set e.cancel so no need to dispose
42
+ if (isPromise<T>(value)) {
43
+ return effect ? value.then(effect) : (value as any);
44
+ } else if (value !== undefined) {
45
+ return effect ? effectValue : Promise.resolve(value);
46
+ } else {
47
+ // Wrap it in a promise
48
+ const promise = new Promise<T2>((resolve) => {
49
+ if (effect) {
50
+ const originalEffect = effect;
51
+ effect = ((value: T) => {
52
+ const effectValue = originalEffect(value);
53
+ resolve(isPromise(effectValue) ? effectValue.then((value) => value as T2) : effectValue);
54
+ }) as any;
55
+ } else {
56
+ effect = resolve as any;
57
+ }
58
+ });
59
+
60
+ return promise;
61
+ }
62
+ }
63
+
64
+ export function when<T, T2>(predicate: Promise<T>, effect: (value: T) => T2): Promise<T2>;
65
+ export function when<T, T2>(predicate: Selector<T>, effect: (value: T) => T2): Promise<T2>;
66
+ export function when<T>(predicate: Selector<T>): Promise<T>;
67
+ export function when<T, T2>(predicate: Selector<T>, effect?: (value: T) => T2): Promise<T | T2> {
68
+ return _when<T, T2>(predicate, effect, false);
69
+ }
70
+ export function whenReady<T, T2>(predicate: Promise<T>, effect: (value: T) => T2): Promise<T2>;
71
+ export function whenReady<T, T2>(predicate: Selector<T>, effect: (value: T) => T2): Promise<T2>;
72
+ export function whenReady<T>(predicate: Selector<T>): Promise<T>;
73
+ export function whenReady<T, T2>(predicate: Selector<T>, effect?: (value: T) => T2): Promise<T | T2> {
74
+ return _when<T, T2>(predicate, effect, true);
75
+ }
@@ -0,0 +1,41 @@
1
+ import { SyncTransform, SyncedGetParams, SyncedOptions, SyncedSetParams } from '@legendapp/state/sync';
2
+ export type CrudAsOption = 'Map' | 'object' | 'first';
3
+ export type CrudResult<T> = T;
4
+ export interface SyncedCrudPropsSingle<TRemote, TLocal> {
5
+ get?: (params: SyncedGetParams) => Promise<CrudResult<TRemote | null>> | CrudResult<TRemote | null>;
6
+ initial?: InitialValue<TLocal, 'first'>;
7
+ as?: never | 'first';
8
+ }
9
+ export interface SyncedCrudPropsMany<TRemote, TLocal, TAsOption extends CrudAsOption> {
10
+ list?: (params: SyncedGetParams) => Promise<CrudResult<TRemote[] | null>> | CrudResult<TRemote[] | null>;
11
+ as?: TAsOption;
12
+ initial?: InitialValue<TLocal, TAsOption>;
13
+ }
14
+ export interface SyncedCrudPropsBase<TRemote extends {
15
+ id: string | number;
16
+ }, TLocal = TRemote> extends Omit<SyncedOptions<TLocal>, 'get' | 'set' | 'transform' | 'initial'> {
17
+ create?(input: TRemote, params: SyncedSetParams<TRemote>): Promise<CrudResult<TRemote> | null | undefined>;
18
+ update?(input: Partial<TRemote>, params: SyncedSetParams<TRemote>): Promise<CrudResult<Partial<TRemote> | null | undefined>>;
19
+ delete?(input: TRemote, params: SyncedSetParams<TRemote>): Promise<CrudResult<any>>;
20
+ onSaved?(saved: TLocal, input: TRemote, isCreate: boolean): Partial<TLocal> | void;
21
+ transform?: SyncTransform<TLocal, TRemote>;
22
+ fieldUpdatedAt?: string;
23
+ fieldCreatedAt?: string;
24
+ updatePartial?: boolean;
25
+ changesSince?: 'all' | 'last-sync';
26
+ generateId?: () => string | number;
27
+ }
28
+ type InitialValue<T, TAsOption extends CrudAsOption> = TAsOption extends 'Map' ? Map<string, T> : TAsOption extends 'object' ? Record<string, T> : TAsOption extends 'first' ? T : T[];
29
+ export type SyncedCrudReturnType<TLocal, TAsOption extends CrudAsOption> = TAsOption extends 'Map' ? Map<string, TLocal> : TAsOption extends 'object' ? Record<string, TLocal> : TAsOption extends 'first' ? TLocal : TLocal[];
30
+ export declare function createTransform<T extends Record<string, any>, T2 extends Record<string, any>>(...keys: (keyof T | {
31
+ from: keyof T;
32
+ to: keyof T2;
33
+ })[]): SyncTransform<T2, T>;
34
+ export declare function combineTransforms<T, T2>(transform1: SyncTransform<T2, T>, ...transforms: Partial<SyncTransform<T2, T>>[]): SyncTransform<T2, T>;
35
+ export declare function syncedCrud<TRemote extends {
36
+ id: string | number;
37
+ }, TLocal = TRemote>(props: SyncedCrudPropsBase<TRemote, TLocal> & SyncedCrudPropsSingle<TRemote, TLocal>): SyncedCrudReturnType<TLocal, 'first'>;
38
+ export declare function syncedCrud<TRemote extends {
39
+ id: string | number;
40
+ }, TLocal = TRemote, TAsOption extends CrudAsOption = 'object'>(props: SyncedCrudPropsBase<TRemote, TLocal> & SyncedCrudPropsMany<TRemote, TLocal, TAsOption>): SyncedCrudReturnType<TLocal, Exclude<TAsOption, 'first'>>;
41
+ export {};