@legendapp/state 2.2.0-next.87 → 2.2.0-next.89

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 (53) hide show
  1. package/config/{enable$get.d.ts → enable$GetSet.d.ts} +2 -2
  2. package/config/{enable$get.js → enable$GetSet.js} +4 -4
  3. package/config/enable$GetSet.js.map +1 -0
  4. package/config/{enable$get.mjs → enable$GetSet.mjs} +4 -4
  5. package/config/enable$GetSet.mjs.map +1 -0
  6. package/config/{enable_peek.d.ts → enable_PeekAssign.d.ts} +2 -2
  7. package/config/{enable_peek.js → enable_PeekAssign.js} +4 -4
  8. package/config/enable_PeekAssign.js.map +1 -0
  9. package/config/{enable_peek.mjs → enable_PeekAssign.mjs} +4 -4
  10. package/config/enable_PeekAssign.mjs.map +1 -0
  11. package/index.js +8 -1
  12. package/index.js.map +1 -1
  13. package/index.mjs +8 -1
  14. package/index.mjs.map +1 -1
  15. package/package.json +9 -9
  16. package/persist.js +2 -1
  17. package/persist.js.map +1 -1
  18. package/persist.mjs +2 -1
  19. package/persist.mjs.map +1 -1
  20. package/src/ObservableObject.ts +8 -1
  21. package/src/config/{enable$get.ts → enable$GetSet.ts} +2 -2
  22. package/src/config/{enable_peek.ts → enable_PeekAssign.ts} +2 -2
  23. package/src/sync/syncHelpers.ts +121 -1
  24. package/src/sync/syncObservable.ts +2 -1
  25. package/src/sync/syncTypes.ts +8 -8
  26. package/src/sync/synced.ts +3 -1
  27. package/src/sync-plugins/crud.ts +21 -89
  28. package/src/sync-plugins/keel.ts +80 -61
  29. package/src/sync-plugins/supabase.ts +43 -51
  30. package/sync-plugins/crud.d.ts +4 -9
  31. package/sync-plugins/crud.js +16 -75
  32. package/sync-plugins/crud.js.map +1 -1
  33. package/sync-plugins/crud.mjs +18 -75
  34. package/sync-plugins/crud.mjs.map +1 -1
  35. package/sync-plugins/keel.d.ts +26 -13
  36. package/sync-plugins/keel.js +12 -39
  37. package/sync-plugins/keel.js.map +1 -1
  38. package/sync-plugins/keel.mjs +13 -40
  39. package/sync-plugins/keel.mjs.map +1 -1
  40. package/sync-plugins/supabase.d.ts +13 -13
  41. package/sync-plugins/supabase.js +3 -21
  42. package/sync-plugins/supabase.js.map +1 -1
  43. package/sync-plugins/supabase.mjs +4 -22
  44. package/sync-plugins/supabase.mjs.map +1 -1
  45. package/sync.d.ts +3 -3
  46. package/sync.js +86 -1
  47. package/sync.js.map +1 -1
  48. package/sync.mjs +85 -3
  49. package/sync.mjs.map +1 -1
  50. package/config/enable$get.js.map +0 -1
  51. package/config/enable$get.mjs.map +0 -1
  52. package/config/enable_peek.js.map +0 -1
  53. package/config/enable_peek.mjs.map +0 -1
@@ -1,13 +1,5 @@
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';
1
+ import { internal, isArray, isNullOrUndefined } from '@legendapp/state';
2
+ import { SyncedGetParams, SyncedOptions, SyncedSetParams, diffObjects, synced } from '@legendapp/state/sync';
11
3
 
12
4
  const { clone } = internal;
13
5
 
@@ -26,17 +18,17 @@ export interface SyncedCrudPropsMany<TRemote, TLocal, TAsOption extends CrudAsOp
26
18
  initial?: InitialValue<TLocal, TAsOption>;
27
19
  }
28
20
  export interface SyncedCrudPropsBase<TRemote extends { id: string | number }, TLocal = TRemote>
29
- extends Omit<SyncedOptions<TLocal>, 'get' | 'set' | 'transform' | 'initial'> {
21
+ extends Omit<SyncedOptions<TRemote, TLocal>, 'get' | 'set' | 'initial'> {
30
22
  create?(input: TRemote, params: SyncedSetParams<TRemote>): Promise<CrudResult<TRemote> | null | undefined>;
31
23
  update?(
32
24
  input: Partial<TRemote>,
33
25
  params: SyncedSetParams<TRemote>,
34
26
  ): Promise<CrudResult<Partial<TRemote> | null | undefined>>;
35
27
  delete?(input: { id: TRemote['id'] }, params: SyncedSetParams<TRemote>): Promise<CrudResult<any>>;
36
- onSaved?(saved: TLocal, input: TRemote, isCreate: boolean): Partial<TLocal> | void;
37
- transform?: SyncTransform<TLocal, TRemote>;
28
+ onSaved?(saved: TLocal, input: TRemote, isCreate: boolean): void;
38
29
  fieldUpdatedAt?: string;
39
30
  fieldCreatedAt?: string;
31
+ fieldDeleted?: string;
40
32
  updatePartial?: boolean;
41
33
  changesSince?: 'all' | 'last-sync';
42
34
  generateId?: () => string | number;
@@ -64,73 +56,6 @@ function transformOut<T1, T2>(data: T1, transform: undefined | ((value: T1) => T
64
56
  return transform ? transform(clone(data)) : data;
65
57
  }
66
58
 
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
59
  function ensureId(obj: { id: string | number }, generateId: () => string | number) {
135
60
  if (!obj.id) {
136
61
  obj.id = generateId();
@@ -165,6 +90,7 @@ export function syncedCrud<
165
90
  transform,
166
91
  fieldCreatedAt,
167
92
  fieldUpdatedAt,
93
+ fieldDeleted,
168
94
  updatePartial,
169
95
  onSaved,
170
96
  mode: modeParam,
@@ -211,7 +137,8 @@ export function syncedCrud<
211
137
  } else {
212
138
  const out: Record<string, any> = asMap ? new Map() : {};
213
139
  transformed.forEach((result: any) => {
214
- const value = result.__deleted ? internal.symbolDelete : result;
140
+ const value =
141
+ result[fieldDeleted as any] || result.__deleted ? internal.symbolDelete : result;
215
142
  asMap ? (out as Map<any, any>).set(result.id, value) : (out[result.id] = value);
216
143
  });
217
144
  return out;
@@ -298,11 +225,7 @@ export function syncedCrud<
298
225
  isCreateGuess = !(fieldCreatedAt || fieldUpdatedAt) && path.length === 1 && !prevAtPath;
299
226
  if (!itemValue) {
300
227
  if (path.length === 1 && prevAtPath) {
301
- if (deleteFn) {
302
- deletes.add(itemKey);
303
- } else {
304
- console.log('[legend-state] missing delete function');
305
- }
228
+ deletes.add(itemKey);
306
229
  }
307
230
  } else {
308
231
  itemsChanged = [[itemKey, itemValue]];
@@ -352,7 +275,7 @@ export function syncedCrud<
352
275
  transform?.load ? transform.load(data as any, 'set') : data
353
276
  ) as any;
354
277
 
355
- const savedOut = onSaved(dataLoaded, input, isCreate);
278
+ const savedOut = diffObjects(input, dataLoaded as any);
356
279
 
357
280
  if (savedOut) {
358
281
  const createdAt = fieldCreatedAt ? savedOut[fieldCreatedAt as keyof TLocal] : undefined;
@@ -366,6 +289,8 @@ export function syncedCrud<
366
289
  updatedAt || createdAt ? +new Date(updatedAt || (createdAt as any)) : undefined,
367
290
  mode: 'merge',
368
291
  });
292
+
293
+ onSaved(dataLoaded, input, isCreate);
369
294
  }
370
295
  }
371
296
  };
@@ -392,8 +317,15 @@ export function syncedCrud<
392
317
  );
393
318
  }
394
319
  }),
395
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
396
- ...Array.from(deletes).map((itemKey) => deleteFn!({ id: itemKey }, params)),
320
+ ...Array.from(deletes).map((id) => {
321
+ if (fieldDeleted && updateFn) {
322
+ updateFn({ id, [fieldDeleted]: true } as any, params);
323
+ } else if (deleteFn) {
324
+ deleteFn({ id }, params);
325
+ } else {
326
+ console.log('[legend-state] missing delete function');
327
+ }
328
+ }),
397
329
  ]);
398
330
  }
399
331
  : undefined;
@@ -1,4 +1,4 @@
1
- import { computeSelector, observable, when, internal } from '@legendapp/state';
1
+ import { computeSelector, observable, when, internal, isFunction, mergeIntoObservable } from '@legendapp/state';
2
2
  import {
3
3
  SyncedOptions,
4
4
  removeNullUndefined,
@@ -44,15 +44,14 @@ type Result<T, U> = NonNullable<Data<T> | Err<U>>;
44
44
 
45
45
  // Keel plugin types
46
46
 
47
- interface GetGetParams {
48
- refresh: () => void;
49
- }
47
+ export interface KeelGetParams {}
50
48
 
51
- interface ListGetParams {
52
- where: { updatedAt?: { after: Date } };
53
- refresh?: () => void;
49
+ export interface KeelListParams<Where = {}> {
50
+ where: { updatedAt?: { after: Date } } & Where;
54
51
  after?: string;
55
52
  first?: number;
53
+ last?: number;
54
+ before?: string;
56
55
  }
57
56
 
58
57
  export interface KeelRealtimePlugin {
@@ -68,9 +67,7 @@ export interface SyncedKeelConfiguration
68
67
  | 'update'
69
68
  | 'delete'
70
69
  | 'onSaved'
71
- | 'transform'
72
70
  | 'updatePartial'
73
- | 'subscribe'
74
71
  | 'fieldCreatedAt'
75
72
  | 'fieldUpdatedAt'
76
73
  > {
@@ -92,23 +89,60 @@ interface PageInfo {
92
89
  totalCount: number;
93
90
  }
94
91
 
95
- interface SyncedKeelPropsMany<TRemote, TLocal, AOption extends CrudAsOption>
92
+ interface SyncedKeelPropsManyBase<TRemote, TLocal, AOption extends CrudAsOption>
96
93
  extends Omit<SyncedCrudPropsMany<TRemote, TLocal, AOption>, 'list'> {
97
- list?: (params: ListGetParams) => Promise<CrudResult<APIResult<{ results: TRemote[]; pageInfo: any }>>>;
98
94
  first?: number;
99
95
  get?: never;
100
96
  }
97
+ interface SyncedKeelPropsManyWhere<TRemote, TLocal, AOption extends CrudAsOption, Where extends Record<string, any>>
98
+ extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
99
+ list?: (params: KeelListParams<NoInfer<Where>>) => Promise<
100
+ CrudResult<
101
+ APIResult<{
102
+ results: TRemote[];
103
+ pageInfo: any;
104
+ }>
105
+ >
106
+ >;
107
+ where?: Where | (() => Where);
108
+ }
109
+ interface SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption extends CrudAsOption>
110
+ extends SyncedKeelPropsManyBase<TRemote, TLocal, AOption> {
111
+ list?: (params: KeelListParams<{}>) => Promise<
112
+ CrudResult<
113
+ APIResult<{
114
+ results: TRemote[];
115
+ pageInfo: any;
116
+ }>
117
+ >
118
+ >;
119
+ where?: never | {};
120
+ }
121
+ type HasAnyKeys<T> = keyof T extends never ? false : true;
122
+
123
+ type SyncedKeelPropsMany<
124
+ TRemote,
125
+ TLocal,
126
+ AOption extends CrudAsOption,
127
+ Where extends Record<string, any>,
128
+ > = HasAnyKeys<Where> extends true
129
+ ? SyncedKeelPropsManyWhere<TRemote, TLocal, AOption, Where>
130
+ : SyncedKeelPropsManyNoWhere<TRemote, TLocal, AOption>;
101
131
 
102
132
  interface SyncedKeelPropsSingle<TRemote, TLocal> extends Omit<SyncedCrudPropsSingle<TRemote, TLocal>, 'get'> {
103
- get?: (params: GetGetParams) => Promise<APIResult<TRemote>>;
133
+ get?: (params: KeelGetParams) => Promise<APIResult<TRemote>>;
104
134
 
105
135
  first?: never;
136
+ where?: never;
106
137
  list?: never;
107
138
  as?: never;
108
139
  }
109
140
 
110
141
  interface SyncedKeelPropsBase<TRemote extends { id: string }, TLocal = TRemote>
111
- extends Omit<SyncedCrudPropsBase<TRemote, TLocal>, 'create' | 'update' | 'delete'> {
142
+ extends Omit<
143
+ SyncedCrudPropsBase<TRemote, TLocal>,
144
+ 'create' | 'update' | 'delete' | 'updatePartial' | 'fieldUpdatedAt' | 'fieldCreatedAt'
145
+ > {
112
146
  create?: (i: NoInfer<Partial<TRemote>>) => Promise<APIResult<NoInfer<TRemote>>>;
113
147
  update?: (params: { where: any; values?: Partial<TRemote> }) => Promise<APIResult<TRemote>>;
114
148
  delete?: (params: { id: string }) => Promise<APIResult<string>>;
@@ -153,13 +187,12 @@ function convertObjectToCreate<TRemote>(item: TRemote): TRemote {
153
187
  }
154
188
 
155
189
  export function configureSyncedKeel(config: SyncedKeelConfiguration) {
156
- const { enabled, realtimePlugin, ...rest } = config;
190
+ const { enabled, realtimePlugin, client, ...rest } = config;
157
191
  Object.assign(keelConfig, removeNullUndefined(rest));
158
192
 
159
193
  if (enabled !== undefined) {
160
194
  isEnabled$.set(enabled);
161
195
  }
162
- const { client } = keelConfig;
163
196
 
164
197
  if (realtimePlugin) {
165
198
  keelConfig.realtimePlugin = realtimePlugin;
@@ -198,13 +231,13 @@ export function configureSyncedKeel(config: SyncedKeelConfiguration) {
198
231
 
199
232
  const NumPerPage = 200;
200
233
  async function getAllPages<TRemote>(
201
- listFn: (params: ListGetParams) => Promise<
234
+ listFn: (params: KeelListParams<any>) => Promise<
202
235
  APIResult<{
203
236
  results: TRemote[];
204
237
  pageInfo: any;
205
238
  }>
206
239
  >,
207
- params: ListGetParams,
240
+ params: KeelListParams,
208
241
  ): Promise<{ results: TRemote[]; subscribe: (params: { refresh: () => void }) => string }> {
209
242
  const allData: TRemote[] = [];
210
243
  let pageInfo: PageInfo | undefined = undefined;
@@ -218,7 +251,7 @@ async function getAllPages<TRemote>(
218
251
  break;
219
252
  }
220
253
  const pageEndCursor = pageInfo?.endCursor;
221
- const paramsWithCursor: ListGetParams = pageEndCursor
254
+ const paramsWithCursor: KeelListParams = pageEndCursor
222
255
  ? { first, ...params, after: pageEndCursor }
223
256
  : { first, ...params };
224
257
  pageInfo = undefined;
@@ -248,13 +281,26 @@ async function getAllPages<TRemote>(
248
281
  export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote>(
249
282
  props: SyncedKeelPropsBase<TRemote, TLocal> & SyncedKeelPropsSingle<TRemote, TLocal>,
250
283
  ): 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>,
284
+ export function syncedKeel<
285
+ TRemote extends { id: string },
286
+ TLocal = TRemote,
287
+ TOption extends CrudAsOption = 'object',
288
+ Where extends Record<string, any> = {},
289
+ >(
290
+ props: SyncedKeelPropsBase<TRemote, TLocal> & SyncedKeelPropsMany<TRemote, TLocal, TOption, Where>,
253
291
  ): SyncedCrudReturnType<TLocal, Exclude<TOption, 'first'>>;
254
- export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOption extends CrudAsOption = 'object'>(
292
+ export function syncedKeel<
293
+ TRemote extends { id: string },
294
+ TLocal = TRemote,
295
+ TOption extends CrudAsOption = 'object',
296
+ Where extends Record<string, any> = {},
297
+ >(
255
298
  props: SyncedKeelPropsBase<TRemote, TLocal> &
256
- (SyncedKeelPropsSingle<TRemote, TLocal> | SyncedKeelPropsMany<TRemote, TLocal, TOption>),
299
+ (SyncedKeelPropsSingle<TRemote, TLocal> | SyncedKeelPropsMany<TRemote, TLocal, TOption, Where>),
257
300
  ): SyncedCrudReturnType<TLocal, TOption> {
301
+ const { realtimePlugin } = keelConfig;
302
+ mergeIntoObservable(props, keelConfig);
303
+
258
304
  const {
259
305
  get: getParam,
260
306
  list: listParam,
@@ -262,23 +308,18 @@ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOp
262
308
  update: updateParam,
263
309
  delete: deleteParam,
264
310
  first,
311
+ where: whereParam,
265
312
  waitFor,
266
- waitForSet,
267
313
  generateId: generateIdParam,
268
314
  ...rest
269
315
  } = props;
270
316
 
271
317
  const { changesSince } = props;
272
318
 
273
- let asType = props.as as TOption;
274
-
275
- if (!asType) {
276
- asType = (getParam ? 'first' : keelConfig.as || undefined) as TOption;
277
- }
319
+ const asType: TOption = getParam ? ('first' as TOption) : props.as!;
278
320
 
279
321
  const generateId = generateIdParam || keelConfig.generateId;
280
322
 
281
- const realtimePlugin = keelConfig.realtimePlugin;
282
323
  let realtimeKeyList: string | undefined = undefined;
283
324
  let realtimeKeyGet: string | undefined = undefined;
284
325
 
@@ -289,12 +330,12 @@ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOp
289
330
  ? async (listParams: SyncedGetParams) => {
290
331
  const { lastSync, refresh } = listParams;
291
332
  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
333
  // 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 };
334
+ const where = Object.assign(
335
+ queryBySync ? { updatedAt: { after: new Date(+new Date(lastSync) + 1) } } : {},
336
+ isFunction(whereParam) ? whereParam() : whereParam,
337
+ );
338
+ const params: KeelListParams = { where, first };
298
339
 
299
340
  // TODO: Error?
300
341
  const { results, subscribe } = await getAllPages(listParam, params);
@@ -323,30 +364,11 @@ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOp
323
364
  }
324
365
  : undefined;
325
366
 
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
-
367
+ const onSaved = (data: TLocal) => {
368
+ if (realtimePlugin && data) {
347
369
  const updatedAt = data[fieldUpdatedAt as keyof TLocal] as Date;
348
370
 
349
- if (updatedAt && realtimePlugin) {
371
+ if (updatedAt) {
350
372
  if (realtimeKeyGet) {
351
373
  realtimePlugin.setLatestChange(realtimeKeyGet, updatedAt);
352
374
  }
@@ -354,8 +376,6 @@ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOp
354
376
  realtimePlugin.setLatestChange(realtimeKeyList, updatedAt);
355
377
  }
356
378
  }
357
-
358
- return savedOut;
359
379
  }
360
380
  };
361
381
 
@@ -421,8 +441,8 @@ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOp
421
441
  }
422
442
  : undefined;
423
443
  const deleteFn = deleteParam
424
- ? async (input: { id: string }, params: SyncedSetParams<TRemote>) => {
425
- const { data, error } = await deleteParam({ id: input.id });
444
+ ? async ({ id }: { id: string }, params: SyncedSetParams<TRemote>) => {
445
+ const { data, error } = await deleteParam({ id });
426
446
 
427
447
  if (error) {
428
448
  handleSetError(error, params, false);
@@ -440,7 +460,6 @@ export function syncedKeel<TRemote extends { id: string }, TLocal = TRemote, TOp
440
460
  update,
441
461
  delete: deleteFn,
442
462
  waitFor: () => isEnabled$.get() && (waitFor ? computeSelector(waitFor) : true),
443
- waitForSet,
444
463
  onSaved,
445
464
  fieldCreatedAt,
446
465
  fieldUpdatedAt,
@@ -1,4 +1,4 @@
1
- import { Observable, computeSelector, mergeIntoObservable, observable, symbolDelete } from '@legendapp/state';
1
+ import { Observable, computeSelector, isObject, mergeIntoObservable, observable, symbolDelete } from '@legendapp/state';
2
2
  import {
3
3
  SyncedOptions,
4
4
  SyncedOptionsGlobal,
@@ -22,20 +22,28 @@ import type { SupabaseClient } from '@supabase/supabase-js';
22
22
  // ? SchemaName
23
23
  // : never;
24
24
 
25
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
26
- type SchemaOf<Client extends SupabaseClient> = Client extends SupabaseClient<infer _, infer __, infer Schema>
25
+ export type SupabaseSchemaOf<Client extends SupabaseClient> = Client extends SupabaseClient<
26
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
+ infer _,
28
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
29
+ infer __,
30
+ infer Schema
31
+ >
27
32
  ? Schema
28
33
  : never;
29
- type TableOf<Client extends SupabaseClient> = SchemaOf<Client>['Tables'];
30
- type CollectionOf<Client extends SupabaseClient> = keyof TableOf<Client>;
31
- type RowOf<Client extends SupabaseClient, Collection extends CollectionOf<Client>> = TableOf<Client>[Collection]['Row'];
34
+ export type SupabaseTableOf<Client extends SupabaseClient> = SupabaseSchemaOf<Client>['Tables'];
35
+ export type SupabaseCollectionOf<Client extends SupabaseClient> = keyof SupabaseTableOf<Client>;
36
+ export type SupabaseRowOf<
37
+ Client extends SupabaseClient,
38
+ Collection extends SupabaseCollectionOf<Client>,
39
+ > = SupabaseTableOf<Client>[Collection]['Row'];
32
40
 
33
41
  export type SyncedSupabaseConfig<T extends { id: string }> = Omit<
34
42
  SyncedCrudPropsBase<T>,
35
- 'create' | 'update' | 'delete' | 'onSaved' | 'transform' | 'updatePartial' | 'subscribe'
43
+ 'create' | 'update' | 'delete'
36
44
  >;
37
45
 
38
- export interface SyncedSupabaseGlobalConfig
46
+ export interface SyncedSupabaseConfiguration
39
47
  extends Omit<SyncedSupabaseConfig<{ id: string }>, 'persist' | keyof SyncedOptions> {
40
48
  persist?: SyncedOptionsGlobal;
41
49
  enabled?: Observable<boolean>;
@@ -44,46 +52,46 @@ export interface SyncedSupabaseGlobalConfig
44
52
 
45
53
  interface SyncedSupabaseProps<
46
54
  Client extends SupabaseClient,
47
- Collection extends CollectionOf<Client>,
55
+ Collection extends SupabaseCollectionOf<Client>,
48
56
  TOption extends CrudAsOption = 'object',
49
- > extends SyncedSupabaseConfig<RowOf<Client, Collection>>,
50
- SyncedCrudPropsMany<RowOf<Client, Collection>, RowOf<Client, Collection>, TOption> {
57
+ > extends SyncedSupabaseConfig<SupabaseRowOf<Client, Collection>>,
58
+ SyncedCrudPropsMany<SupabaseRowOf<Client, Collection>, SupabaseRowOf<Client, Collection>, TOption> {
51
59
  supabase: Client;
52
60
  collection: Collection;
53
61
  select?: (
54
- query: PostgrestQueryBuilder<SchemaOf<Client>, TableOf<Client>[Collection], Collection>,
62
+ query: PostgrestQueryBuilder<SupabaseSchemaOf<Client>, SupabaseTableOf<Client>[Collection], Collection>,
55
63
  ) => PostgrestFilterBuilder<
56
- SchemaOf<Client>,
57
- RowOf<Client, Collection>,
58
- RowOf<Client, Collection>[],
64
+ SupabaseSchemaOf<Client>,
65
+ SupabaseRowOf<Client, Collection>,
66
+ SupabaseRowOf<Client, Collection>[],
59
67
  Collection,
60
68
  []
61
69
  >;
62
70
  filter?: (
63
71
  select: PostgrestFilterBuilder<
64
- SchemaOf<Client>,
65
- RowOf<Client, Collection>,
66
- RowOf<Client, Collection>[],
72
+ SupabaseSchemaOf<Client>,
73
+ SupabaseRowOf<Client, Collection>,
74
+ SupabaseRowOf<Client, Collection>[],
67
75
  Collection,
68
76
  []
69
77
  >,
70
78
  params: SyncedGetParams,
71
79
  ) => PostgrestFilterBuilder<
72
- SchemaOf<Client>,
73
- RowOf<Client, Collection>,
74
- RowOf<Client, Collection>[],
80
+ SupabaseSchemaOf<Client>,
81
+ SupabaseRowOf<Client, Collection>,
82
+ SupabaseRowOf<Client, Collection>[],
75
83
  Collection,
76
84
  []
77
85
  >;
78
86
  actions?: ('create' | 'read' | 'update' | 'delete')[];
79
- realtime?: { schema?: string; filter?: string };
87
+ realtime?: boolean | { schema?: string; filter?: string };
80
88
  }
81
89
 
82
90
  let channelNum = 1;
83
- const supabaseConfig: SyncedSupabaseGlobalConfig = {};
91
+ const supabaseConfig: SyncedSupabaseConfiguration = {};
84
92
  const isEnabled$ = observable(true);
85
93
 
86
- export function configureSyncedSupabase(config: SyncedSupabaseGlobalConfig) {
94
+ export function configureSyncedSupabase(config: SyncedSupabaseConfiguration) {
87
95
  const { enabled, ...rest } = config;
88
96
  if (enabled !== undefined) {
89
97
  isEnabled$.set(enabled);
@@ -93,9 +101,11 @@ export function configureSyncedSupabase(config: SyncedSupabaseGlobalConfig) {
93
101
 
94
102
  export function syncedSupabase<
95
103
  Client extends SupabaseClient,
96
- Collection extends CollectionOf<Client> & string,
104
+ Collection extends SupabaseCollectionOf<Client> & string,
97
105
  AsOption extends CrudAsOption = 'object',
98
- >(props: SyncedSupabaseProps<Client, Collection, AsOption>): SyncedCrudReturnType<RowOf<Client, Collection>, AsOption> {
106
+ >(
107
+ props: SyncedSupabaseProps<Client, Collection, AsOption>,
108
+ ): SyncedCrudReturnType<SupabaseRowOf<Client, Collection>, AsOption> {
99
109
  mergeIntoObservable(props, supabaseConfig);
100
110
  const {
101
111
  supabase: client,
@@ -122,7 +132,6 @@ export function syncedSupabase<
122
132
  const from = client.from(collection);
123
133
  let select = selectFn ? selectFn(from) : from.select();
124
134
  if (changesSince === 'last-sync') {
125
- select = select.neq('deleted', true);
126
135
  if (lastSync) {
127
136
  const date = new Date(lastSync).toISOString();
128
137
  select = select.or(
@@ -140,11 +149,11 @@ export function syncedSupabase<
140
149
  if (error) {
141
150
  throw new Error(error?.message);
142
151
  }
143
- return (data! || []) as RowOf<Client, Collection>[];
152
+ return (data! || []) as SupabaseRowOf<Client, Collection>[];
144
153
  }
145
154
  : undefined;
146
155
 
147
- const upsert = async (input: RowOf<Client, Collection>) => {
156
+ const upsert = async (input: SupabaseRowOf<Client, Collection>) => {
148
157
  const res = await client.from(collection).upsert(input).select();
149
158
  const { data, error } = res;
150
159
  if (data) {
@@ -158,12 +167,10 @@ export function syncedSupabase<
158
167
  const update = !actions || actions.includes('update') ? upsert : undefined;
159
168
  const deleteFn =
160
169
  !actions || actions.includes('delete')
161
- ? async (input: { id: RowOf<Client, Collection>['id'] }) => {
170
+ ? async (input: { id: SupabaseRowOf<Client, Collection>['id'] }) => {
162
171
  const id = input.id;
163
172
  const from = client.from(collection);
164
- const res = await (changesSince === 'last-sync' ? from.update({ deleted: true }) : from.delete())
165
- .eq('id', id)
166
- .select();
173
+ const res = await from.delete().eq('id', id).select();
167
174
  const { data, error } = res;
168
175
  if (data) {
169
176
  const created = data[0];
@@ -175,7 +182,7 @@ export function syncedSupabase<
175
182
  : undefined;
176
183
  const subscribe = realtime
177
184
  ? ({ node, value$, update }: SyncedSubscribeParams) => {
178
- const { filter, schema } = realtime;
185
+ const { filter, schema } = (isObject(realtime) ? realtime : {}) as { schema?: string; filter?: string };
179
186
  const channel = client
180
187
  .channel(`LS_${node.key || ''}${channelNum++}`)
181
188
  .on(
@@ -222,31 +229,16 @@ export function syncedSupabase<
222
229
  }
223
230
  : undefined;
224
231
 
225
- return syncedCrud<RowOf<Client, Collection>, RowOf<Client, Collection>, AsOption>({
232
+ return syncedCrud<SupabaseRowOf<Client, Collection>, SupabaseRowOf<Client, Collection>, AsOption>({
226
233
  ...rest,
227
234
  list,
228
235
  create,
229
236
  update,
230
237
  delete: deleteFn,
231
- onSaved: (saved) => {
232
- // Update the local timestamps with server response
233
- if (fieldCreatedAt || fieldUpdatedAt) {
234
- const ret: any = {
235
- id: saved.id,
236
- };
237
- if (fieldCreatedAt) {
238
- ret[fieldCreatedAt] = fieldCreatedAt;
239
- }
240
- if (fieldUpdatedAt) {
241
- ret[fieldUpdatedAt] = fieldUpdatedAt;
242
- }
243
- return ret;
244
- }
245
- },
246
238
  subscribe,
247
239
  fieldCreatedAt,
248
240
  fieldUpdatedAt,
249
- updatePartial: true,
241
+ updatePartial: false,
250
242
  generateId,
251
243
  waitFor: () => isEnabled$.get() && (waitFor ? computeSelector(waitFor) : true),
252
244
  waitForSet,