@livequery/client 2.0.23 → 2.0.24

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 (2) hide show
  1. package/build/Collection.js +61 -70
  2. package/package.json +1 -1
@@ -42,25 +42,23 @@ export class CollectionObservable extends BehaviorSubject {
42
42
  set_realtime(realtime) {
43
43
  this.collection_options.realtime = realtime;
44
44
  }
45
- #sync(stream, from_local = false, direction) {
45
+ #sync(stream, from, direction) {
46
+ if (from == 'realtime' && this.collection_options.realtime == false)
47
+ return;
46
48
  const state = this.getValue();
47
- const realtime = this.collection_options.realtime ?? true;
48
- const actions = { update: false, reindex: false };
49
+ const actions = { reindex: false };
49
50
  for (const { data, error, code, message, } of stream) {
50
- if (!from_local) {
51
+ if (from == 'request') {
51
52
  state.loading = false;
52
- actions.update = true;
53
53
  }
54
54
  // Error & paging
55
55
  if (error) {
56
56
  state.error = true;
57
57
  state.code = code;
58
58
  state.message = message;
59
- actions.update = true;
60
59
  }
61
60
  if (data?.summary) {
62
61
  state.summary = data.summary;
63
- actions.update = true;
64
62
  }
65
63
  // Sync
66
64
  for (const change of data?.changes || []) {
@@ -72,35 +70,32 @@ export class CollectionObservable extends BehaviorSubject {
72
70
  if (index == -1 && type == 'added') {
73
71
  if (
74
72
  // Is first value from HTTP query
75
- true
73
+ from == 'request'
76
74
  || (
77
75
  // Is realtime update that match filters
78
- (realtime || from_local) && Object
76
+ from == 'realtime' && Object
79
77
  .keys(state.options || {})
80
78
  .filter(key => !key.includes('_'))
81
79
  .every(key => {
80
+ const [field, expression] = key.split(':');
81
+ const a = payload[field];
82
+ const b = state.options?.[field];
83
+ const map = {
84
+ 'default': () => a == b,
85
+ eq: () => a == b,
86
+ ne: () => a != b,
87
+ lt: () => typeof a == 'number' && typeof b == 'number' && a < b,
88
+ lte: () => typeof a == 'number' && typeof b == 'number' && a <= b,
89
+ gt: () => typeof a == 'number' && typeof b == 'number' && a > b,
90
+ gte: () => typeof a == 'number' && typeof b == 'number' && a >= b,
91
+ in: () => Array.isArray(a) && a?.includes(b),
92
+ like: () => typeof a == 'string' && a.includes(`${b}`),
93
+ between: () => typeof a == 'number' && Array.isArray(b) && typeof b[0] == 'number' && b[0] <= a && typeof b[1] == 'number' && a <= b[1]
94
+ };
82
95
  try {
83
- const [field, expression] = key.split(':');
84
- const a = payload[field];
85
- const b = state.options?.[field];
86
- if (!expression)
87
- return a == b;
88
- if (expression == 'ne')
89
- return a != b;
90
- if (expression == 'lt')
91
- return typeof a == 'number' && typeof b == 'number' && a < b;
92
- if (expression == 'lte')
93
- return typeof a == 'number' && typeof b == 'number' && a <= b;
94
- if (expression == 'gt')
95
- return typeof a == 'number' && typeof b == 'number' && a > b;
96
- if (expression == 'gte')
97
- return typeof a == 'number' && typeof b == 'number' && a >= b;
98
- if (expression == 'in' || expression == 'like')
99
- return Array.isArray(a) && a?.includes(b);
100
- if (expression == 'between') {
101
- const [x, y] = b;
102
- return x <= a && a <= y;
103
- }
96
+ const fn = map[expression || 'default'];
97
+ if (fn)
98
+ return fn();
104
99
  }
105
100
  catch (e) { }
106
101
  return false;
@@ -119,12 +114,10 @@ export class CollectionObservable extends BehaviorSubject {
119
114
  };
120
115
  direction == 'forward' ? state.items.push(item) : state.items.unshift(item);
121
116
  actions.reindex = true;
122
- actions.update = true;
123
117
  }
124
118
  }
125
- if (index >= 0 && (realtime || from_local)) {
119
+ if (index >= 0) {
126
120
  if (type == 'added' || type == 'modified') {
127
- actions.update = true;
128
121
  const sort_key_value_updated = this.#sorters.some(({ key }) => {
129
122
  const value = payload[key];
130
123
  if (typeof value == 'string' || typeof value == 'number')
@@ -142,7 +135,6 @@ export class CollectionObservable extends BehaviorSubject {
142
135
  };
143
136
  }
144
137
  if (type == 'removed') {
145
- actions.update = true;
146
138
  state.items.splice(index, 1);
147
139
  for (const [document_id, i] of this.#IdMap) {
148
140
  i == index && this.#IdMap.delete(document_id);
@@ -206,23 +198,30 @@ export class CollectionObservable extends BehaviorSubject {
206
198
  }
207
199
  };
208
200
  }
209
- actions.update && this.next(state);
201
+ this.next(state);
210
202
  }
211
203
  fetch_data(options = {}, loading, flush = false) {
204
+ if (flush) {
205
+ this.#pages.clear();
206
+ this.#queries.forEach(s => s.unsubscribe());
207
+ this.#queries.clear();
208
+ this.#IdMap.clear();
209
+ }
212
210
  if (!this.ref)
213
211
  return;
214
212
  if (this.#refs.length == 0)
215
213
  return;
216
- if (this.getValue().loading)
214
+ if (this.getValue().loading && !flush)
217
215
  return;
218
- this.collection_options.options = options;
219
- this.#sorters = Object.keys(options).filter(k => k.endsWith(':sort')).map(k => {
220
- const key = k.split(':sort')[0];
221
- const order = options[k] == 1 ? 1 : -1;
222
- return { key, order };
216
+ const remain_data_refs = this.#refs.filter(ref => {
217
+ const paging = this.#pages.get(ref);
218
+ if (!paging)
219
+ return true;
220
+ return loading == 'forward' ? paging.has?.next : paging.has?.prev;
223
221
  });
224
- this.#sorters.every(a => a.key != 'id') && this.#sorters.push({ key: 'id', order: -1 });
225
- const state = {
222
+ if (remain_data_refs.length == 0)
223
+ return;
224
+ this.next({
226
225
  ...this.getValue(),
227
226
  items: flush ? [] : this.getValue().items,
228
227
  loading,
@@ -230,23 +229,14 @@ export class CollectionObservable extends BehaviorSubject {
230
229
  ...this.getValue().options || {},
231
230
  ...options
232
231
  }
233
- };
234
- if (flush) {
235
- this.#pages.clear();
236
- this.#queries.forEach(s => s.unsubscribe());
237
- this.#queries.clear();
238
- this.#IdMap.clear();
239
- }
240
- const remain_data_refs = this.#refs.filter(ref => {
241
- const paging = this.#pages.get(ref);
242
- if (!paging)
243
- return true;
244
- return loading == 'forward' ? paging.has?.next : paging.has?.prev;
245
232
  });
246
- const no_more_data = !flush && (remain_data_refs.length == 0 || this.getValue().loading);
247
- if (no_more_data)
248
- return;
249
- this.next(state);
233
+ this.collection_options.options = options;
234
+ this.#sorters = Object.keys(options).filter(k => k.endsWith(':sort')).map(k => {
235
+ const key = k.split(':sort')[0];
236
+ const order = options[k] == 1 ? 1 : -1;
237
+ return { key, order };
238
+ });
239
+ this.#sorters.every(a => a.key != 'id') && this.#sorters.push({ key: 'id', order: -1 });
250
240
  const queries = remain_data_refs.map((ref, index) => {
251
241
  const cursor = this.#pages.get(ref)?.cursor;
252
242
  const opts = {
@@ -262,8 +252,8 @@ export class CollectionObservable extends BehaviorSubject {
262
252
  ref
263
253
  })), share());
264
254
  });
265
- const first_values = merge(...queries.map(q => q.pipe(filter(r => !!r.data?.paging || !!r.error), first()))).pipe(toArray(), tap(list => this.#sync(list, false, loading))).subscribe();
266
- const subscription = merge(...queries.map(q => q.pipe(skip(1)))).pipe(bufferTime(this.collection_options?.sync_delay || 500), filter(list => list.length > 0), map(data => this.#sync(data, false, loading)), finalize(() => first_values.unsubscribe())).subscribe();
255
+ const first_values = merge(...queries.map(q => q.pipe(filter(r => !!r.data?.paging || !!r.error), first()))).pipe(toArray(), tap(list => this.#sync(list, 'request', loading))).subscribe();
256
+ const subscription = merge(...queries.map(q => q.pipe(skip(1)))).pipe(bufferTime(this.collection_options?.sync_delay || 500), filter(list => list.length > 0), map(data => this.#sync(data, 'realtime', loading)), finalize(() => first_values.unsubscribe())).subscribe();
267
257
  this.#queries.add(subscription);
268
258
  }
269
259
  reset() {
@@ -282,17 +272,18 @@ export class CollectionObservable extends BehaviorSubject {
282
272
  }
283
273
  #find_ref_by_id(id) {
284
274
  if (!id || !this.ref)
285
- return { ref: this.ref, collection_ref: this.ref };
275
+ return { ref: this.ref, collection_ref: this.ref, doc: null };
286
276
  const index = this.#IdMap.get(id);
287
277
  if (index == undefined)
288
278
  return {};
289
- const origin_ref = this.getValue().items[index].__ref;
279
+ const doc = this.getValue().items[index];
280
+ const origin_ref = doc.__ref;
290
281
  if (!origin_ref)
291
282
  throw 'COLLECTION_REF_NOT_FOUND';
292
283
  const refs = origin_ref.split('/');
293
284
  const collection_ref = refs.slice(0, refs.length - (refs.length % 2 == 1 ? 0 : 1)).join('/');
294
285
  const ref = `${collection_ref}/${id}`;
295
- return { ref, id, collection_ref, index };
286
+ return { ref, id, collection_ref, index, doc };
296
287
  }
297
288
  async add(payload) {
298
289
  const r = await this.collection_options.transporter.add(`${this.ref}`, payload);
@@ -302,7 +293,7 @@ export class CollectionObservable extends BehaviorSubject {
302
293
  return r;
303
294
  }
304
295
  async update({ id: update_payload_id, ...payload }) {
305
- const { id, ref } = this.#find_ref_by_id(update_payload_id);
296
+ const { id, ref, doc } = this.#find_ref_by_id(update_payload_id);
306
297
  if (!ref)
307
298
  return;
308
299
  // Trigger local update
@@ -315,7 +306,7 @@ export class CollectionObservable extends BehaviorSubject {
315
306
  type: 'modified'
316
307
  }]
317
308
  }
318
- }], true);
309
+ }], 'local');
319
310
  try {
320
311
  return await this.collection_options.transporter.update(ref, payload);
321
312
  }
@@ -324,12 +315,12 @@ export class CollectionObservable extends BehaviorSubject {
324
315
  ref,
325
316
  data: {
326
317
  changes: [{
327
- data: { id, __updating: false },
318
+ data: { ...doc || {}, id, __updating: false },
328
319
  ref,
329
320
  type: 'modified'
330
321
  }]
331
322
  }
332
- }], true);
323
+ }], 'local');
333
324
  throw e;
334
325
  }
335
326
  }
@@ -346,7 +337,7 @@ export class CollectionObservable extends BehaviorSubject {
346
337
  type: 'modified'
347
338
  }]
348
339
  }
349
- }], true);
340
+ }], 'local');
350
341
  // Trigger
351
342
  try {
352
343
  return await this.collection_options.transporter.remove(ref);
@@ -361,7 +352,7 @@ export class CollectionObservable extends BehaviorSubject {
361
352
  type: 'modified'
362
353
  }]
363
354
  }
364
- }], true);
355
+ }], 'local');
365
356
  throw e;
366
357
  }
367
358
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "repository": {
5
5
  "url": "https://github.com/livequery/client"
6
6
  },
7
- "version": "2.0.23",
7
+ "version": "2.0.24",
8
8
  "description": "",
9
9
  "main": "build/index.js",
10
10
  "types": "build/index.d.ts",