@livequery/client 2.0.30 → 2.0.104

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 (51) hide show
  1. package/README.md +477 -0
  2. package/dist/LivequeryCollection.d.ts +92 -0
  3. package/dist/LivequeryCollection.d.ts.map +1 -0
  4. package/dist/LivequeryCollection.js +231 -0
  5. package/dist/LivequeryCollection.js.map +1 -0
  6. package/dist/LivequeryCore.d.ts +67 -0
  7. package/dist/LivequeryCore.d.ts.map +1 -0
  8. package/dist/LivequeryCore.js +343 -0
  9. package/dist/LivequeryCore.js.map +1 -0
  10. package/dist/LivequeryDocument.d.ts +23 -0
  11. package/dist/LivequeryDocument.d.ts.map +1 -0
  12. package/dist/LivequeryDocument.js +22 -0
  13. package/dist/LivequeryDocument.js.map +1 -0
  14. package/dist/LivequeryMemoryStorage.d.ts +14 -0
  15. package/dist/LivequeryMemoryStorage.d.ts.map +1 -0
  16. package/dist/LivequeryMemoryStorage.js +89 -0
  17. package/dist/LivequeryMemoryStorage.js.map +1 -0
  18. package/dist/LivequeryStorge.d.ts +12 -0
  19. package/dist/LivequeryStorge.d.ts.map +1 -0
  20. package/dist/LivequeryStorge.js +2 -0
  21. package/dist/LivequeryStorge.js.map +1 -0
  22. package/dist/LivequeryTransporter.d.ts +22 -0
  23. package/dist/LivequeryTransporter.d.ts.map +1 -0
  24. package/dist/LivequeryTransporter.js +2 -0
  25. package/dist/LivequeryTransporter.js.map +1 -0
  26. package/dist/helpers/filterDocs.d.ts +5 -0
  27. package/dist/helpers/filterDocs.d.ts.map +1 -0
  28. package/dist/helpers/filterDocs.js +80 -0
  29. package/dist/helpers/filterDocs.js.map +1 -0
  30. package/dist/helpers/tryCatch.d.ts +2 -0
  31. package/dist/helpers/tryCatch.d.ts.map +1 -0
  32. package/dist/helpers/tryCatch.js +10 -0
  33. package/dist/helpers/tryCatch.js.map +1 -0
  34. package/dist/helpers/whenCompleted.d.ts +3 -0
  35. package/dist/helpers/whenCompleted.d.ts.map +1 -0
  36. package/dist/helpers/whenCompleted.js +5 -0
  37. package/dist/helpers/whenCompleted.js.map +1 -0
  38. package/dist/index.d.ts +9 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +9 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/types.d.ts +70 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +2 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +95 -18
  47. package/build/Collection.d.ts +0 -52
  48. package/build/Collection.js +0 -367
  49. package/build/index.d.ts +0 -1
  50. package/build/index.js +0 -1
  51. package/build/tsconfig.tsbuildinfo +0 -1
package/package.json CHANGED
@@ -1,29 +1,106 @@
1
1
  {
2
2
  "name": "@livequery/client",
3
+ "version": "2.0.104",
3
4
  "type": "module",
4
- "repository": {
5
- "url": "https://github.com/livequery/client"
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./LivequeryCollection": {
15
+ "types": "./dist/LivequeryCollection.d.ts",
16
+ "import": "./dist/LivequeryCollection.js",
17
+ "default": "./dist/LivequeryCollection.js"
18
+ },
19
+ "./LivequeryCore": {
20
+ "types": "./dist/LivequeryCore.d.ts",
21
+ "import": "./dist/LivequeryCore.js",
22
+ "default": "./dist/LivequeryCore.js"
23
+ },
24
+ "./LivequeryDocument": {
25
+ "types": "./dist/LivequeryDocument.d.ts",
26
+ "import": "./dist/LivequeryDocument.js",
27
+ "default": "./dist/LivequeryDocument.js"
28
+ },
29
+ "./LivequeryMemoryStorage": {
30
+ "types": "./dist/LivequeryMemoryStorage.d.ts",
31
+ "import": "./dist/LivequeryMemoryStorage.js",
32
+ "default": "./dist/LivequeryMemoryStorage.js"
33
+ },
34
+ "./LivequeryStorge": {
35
+ "types": "./dist/LivequeryStorge.d.ts",
36
+ "import": "./dist/LivequeryStorge.js",
37
+ "default": "./dist/LivequeryStorge.js"
38
+ },
39
+ "./LivequeryTransporter": {
40
+ "types": "./dist/LivequeryTransporter.d.ts",
41
+ "import": "./dist/LivequeryTransporter.js",
42
+ "default": "./dist/LivequeryTransporter.js"
43
+ },
44
+ "./types": {
45
+ "types": "./dist/types.d.ts",
46
+ "import": "./dist/types.js",
47
+ "default": "./dist/types.js"
48
+ },
49
+ "./helpers/filterDocs": {
50
+ "types": "./dist/helpers/filterDocs.d.ts",
51
+ "import": "./dist/helpers/filterDocs.js",
52
+ "default": "./dist/helpers/filterDocs.js"
53
+ },
54
+ "./package.json": "./package.json"
55
+ },
56
+ "typesVersions": {
57
+ "*": {
58
+ "LivequeryCollection": [
59
+ "./dist/LivequeryCollection.d.ts"
60
+ ],
61
+ "LivequeryCore": [
62
+ "./dist/LivequeryCore.d.ts"
63
+ ],
64
+ "LivequeryDocument": [
65
+ "./dist/LivequeryDocument.d.ts"
66
+ ],
67
+ "LivequeryMemoryStorage": [
68
+ "./dist/LivequeryMemoryStorage.d.ts"
69
+ ],
70
+ "LivequeryStorge": [
71
+ "./dist/LivequeryStorge.d.ts"
72
+ ],
73
+ "LivequeryTransporter": [
74
+ "./dist/LivequeryTransporter.d.ts"
75
+ ],
76
+ "types": [
77
+ "./dist/types.d.ts"
78
+ ],
79
+ "helpers/filterDocs": [
80
+ "./dist/helpers/filterDocs.d.ts"
81
+ ]
82
+ }
6
83
  },
7
- "version": "2.0.30",
8
- "description": "",
9
- "main": "build/index.js",
10
- "types": "build/index.d.ts",
11
84
  "files": [
12
- "build/**/*"
85
+ "dist"
13
86
  ],
14
- "dependencies": {
15
- "uuid": "^8.3.2"
87
+ "sideEffects": false,
88
+ "scripts": {
89
+ "clean": "rm -rf dist",
90
+ "build:js": "bunx tsc -p tsconfig.build.json --emitDeclarationOnly false --declaration false --declarationMap false",
91
+ "build:types": "bunx tsc -p tsconfig.build.json --emitDeclarationOnly",
92
+ "build": "bun run clean && bun run build:js && bun run build:types",
93
+ "build:watch": "bunx tsc -p tsconfig.build.json --watch --preserveWatchOutput",
94
+ "prepublishOnly": "bun run build"
16
95
  },
17
96
  "devDependencies": {
18
- "typescript": "^5.6.3",
19
- "rxjs": "^7.8.1",
20
- "@livequery/types": "^2.0.22"
97
+ "@types/bun": "latest",
98
+ "@types/chrome": "^0.1.38",
99
+ "typescript": "^5"
21
100
  },
22
- "scripts": {
23
- "test": "echo \"Error: no test specified\" && exit 1",
24
- "build": "rm -rf build; tsc -b .",
25
- "deploy": "yarn build; git add .; git commit -m \"Update\"; git push origin master; npm publish --access public"
101
+ "dependencies": {
102
+ "rxjs": "^7.8.2",
103
+ "uuidv7": "^1.2.1"
26
104
  },
27
- "author": "",
28
- "license": "ISC"
105
+ "private": false
29
106
  }
@@ -1,52 +0,0 @@
1
- import { Subject, BehaviorSubject } from 'rxjs';
2
- import { LivequeryBaseEntity, QueryOption, Transporter, UpdatedData, Paging, Response } from '@livequery/types';
3
- export type LoadingIndicator = false | 'backward' | 'forward' | 'both';
4
- export type CollectionOption<T extends LivequeryBaseEntity = LivequeryBaseEntity> = {
5
- transporter: Transporter;
6
- sync_delay?: number;
7
- options?: Partial<QueryOption<T>>;
8
- realtime?: boolean;
9
- };
10
- export type SmartQueryItem<T> = T & {
11
- __remove: Function;
12
- __removing: boolean;
13
- __update: (data: Partial<T>) => any;
14
- __updating: boolean;
15
- __adding: boolean;
16
- __trigger: <R extends {}>(name: string, payload?: any, query?: any) => Promise<Response<R>>;
17
- __ref: string;
18
- toJson: () => T;
19
- };
20
- export type CollectionStream<T extends LivequeryBaseEntity = LivequeryBaseEntity> = {
21
- n: number;
22
- items: SmartQueryItem<T>[];
23
- paging: Partial<Paging>;
24
- loading?: LoadingIndicator;
25
- options: Partial<QueryOption<T>>;
26
- error?: boolean;
27
- code?: string;
28
- message?: string;
29
- summary?: {
30
- [key: string]: any;
31
- };
32
- };
33
- export declare class CollectionObservable<T extends LivequeryBaseEntity = LivequeryBaseEntity> extends BehaviorSubject<CollectionStream<T>> {
34
- #private;
35
- private ref;
36
- private collection_options;
37
- readonly $changes: Subject<UpdatedData<T>>;
38
- unsubscribe(): void;
39
- constructor(ref: string | false | null | '' | undefined, collection_options: CollectionOption<T>);
40
- set_realtime(realtime: boolean): void;
41
- private fetch_data;
42
- reset(): void;
43
- fetch_more(): void;
44
- fetch_prev(): void;
45
- filter(filters: Partial<QueryOption<T>>): void;
46
- add(payload: Partial<T>): Promise<import("@livequery/types").DocumentResponse<T & LivequeryBaseEntity>>;
47
- update({ id: update_payload_id, ...payload }: Partial<T>): Promise<import("@livequery/types").DocumentResponse<T & LivequeryBaseEntity> | undefined>;
48
- remove(remove_document_id?: string): Promise<import("@livequery/types").DocumentResponse<LivequeryBaseEntity> | undefined>;
49
- trigger<R extends {}>(name: string, payload?: object, trigger_document_id?: string, query?: {
50
- [key: string]: string | number | boolean;
51
- }): Promise<Response<R>>;
52
- }
@@ -1,367 +0,0 @@
1
- import { Subject, merge, BehaviorSubject } from 'rxjs';
2
- import { bufferTime, filter, finalize, first, map, share, skip, tap, toArray } from 'rxjs/operators';
3
- export class CollectionObservable extends BehaviorSubject {
4
- ref;
5
- collection_options;
6
- $changes = new Subject();
7
- #pages = new Map();
8
- #queries = new Set();
9
- #sorters = new Array;
10
- #IdMap = new Map();
11
- #refs = [];
12
- unsubscribe() {
13
- super.unsubscribe();
14
- this.#queries.forEach(s => s.unsubscribe());
15
- }
16
- constructor(ref, collection_options) {
17
- super({
18
- n: 0,
19
- items: [],
20
- loading: false,
21
- paging: {},
22
- options: collection_options.options || {}
23
- });
24
- this.ref = ref;
25
- this.collection_options = collection_options;
26
- if (ref && (ref.startsWith('/') || ref.endsWith('/')))
27
- throw 'INVAILD_REF_FORMAT';
28
- this.#refs = this.#ref_parser(ref);
29
- }
30
- #ref_parser(path) {
31
- if (!path)
32
- return [];
33
- const refs_builder = (paths) => {
34
- const [a, b, ...c] = paths;
35
- if (!b)
36
- return paths;
37
- const r = a.map(aa => b.map(bb => `${aa}/${bb}`)).flat(2);
38
- const d = [r, ...c];
39
- return refs_builder(d);
40
- };
41
- return path.split(',').map(f => refs_builder(f.trim().split('/').map(l => l.split('|')))).flat(2);
42
- }
43
- set_realtime(realtime) {
44
- this.collection_options.realtime = realtime;
45
- }
46
- #sync(stream, from, direction) {
47
- if (from == 'realtime' && this.collection_options.realtime == false)
48
- return;
49
- const state = this.getValue();
50
- const actions = { reindex: false };
51
- for (const { data, error, code, message, } of stream) {
52
- if (from == 'request') {
53
- state.loading = false;
54
- state.n = state.n + 1;
55
- }
56
- // Error & paging
57
- if (error) {
58
- state.error = true;
59
- state.code = code;
60
- state.message = message;
61
- }
62
- if (data?.summary) {
63
- state.summary = data.summary;
64
- }
65
- // Sync
66
- for (const change of data?.changes || []) {
67
- if (!change?.data?.id)
68
- continue;
69
- const { data: payload, type } = change;
70
- this.$changes.next(change);
71
- const index = this.#IdMap.get(payload.id) ?? -1;
72
- if (index == -1 && type == 'added') {
73
- if (
74
- // Is first value from HTTP query
75
- from == 'request'
76
- || (
77
- // Is realtime update that match filters
78
- from == 'realtime' && Object
79
- .keys(state.options || {})
80
- .filter(key => !key.includes('_'))
81
- .every(key => {
82
- const [field, expression] = key.split(':');
83
- const a = payload[field];
84
- const b = state.options?.[field];
85
- const map = {
86
- 'default': () => a == b,
87
- eq: () => a == b,
88
- ne: () => a != b,
89
- lt: () => typeof a == 'number' && typeof b == 'number' && a < b,
90
- lte: () => typeof a == 'number' && typeof b == 'number' && a <= b,
91
- gt: () => typeof a == 'number' && typeof b == 'number' && a > b,
92
- gte: () => typeof a == 'number' && typeof b == 'number' && a >= b,
93
- in: () => Array.isArray(a) && a?.includes(b),
94
- like: () => typeof a == 'string' && a.includes(`${b}`),
95
- between: () => typeof a == 'number' && Array.isArray(b) && typeof b[0] == 'number' && b[0] <= a && typeof b[1] == 'number' && a <= b[1]
96
- };
97
- try {
98
- const fn = map[expression || 'default'];
99
- if (fn)
100
- return fn();
101
- }
102
- catch (e) { }
103
- return false;
104
- }))) {
105
- const item = {
106
- ...payload,
107
- id: payload.id || payload._id,
108
- __adding: false,
109
- __updating: false,
110
- __removing: false,
111
- __remove: () => this.remove(payload?.id),
112
- __trigger: (name, input = undefined, query) => this.trigger(name, input, payload?.id, query),
113
- __update: (input) => this.update({ ...input, id: payload?.id }),
114
- __ref: change.ref,
115
- toJson: () => payload
116
- };
117
- direction == 'forward' ? state.items.push(item) : state.items.unshift(item);
118
- actions.reindex = true;
119
- }
120
- }
121
- if (index >= 0) {
122
- if (type == 'added' || type == 'modified') {
123
- const sort_key_value_updated = this.#sorters.some(({ key }) => {
124
- const value = payload[key];
125
- if (typeof value == 'string' || typeof value == 'number')
126
- return true;
127
- });
128
- if (sort_key_value_updated) {
129
- actions.reindex = true;
130
- }
131
- state.items[index] = {
132
- ...state.items[index],
133
- __adding: false,
134
- __updating: false,
135
- __removing: false,
136
- ...payload
137
- };
138
- }
139
- if (type == 'removed') {
140
- state.items.splice(index, 1);
141
- for (const [document_id, i] of this.#IdMap) {
142
- i == index && this.#IdMap.delete(document_id);
143
- i > index && this.#IdMap.set(document_id, i - 1);
144
- }
145
- }
146
- }
147
- }
148
- }
149
- if (actions.reindex) {
150
- state.items = state.items.sort((a, b) => {
151
- for (const { key, order } of this.#sorters) {
152
- const aa = a[key];
153
- const bb = b[key];
154
- if (typeof aa == 'number' && typeof bb == 'number') {
155
- const rs = aa - bb;
156
- if (rs == 0)
157
- continue;
158
- return rs * order;
159
- }
160
- if (typeof aa == 'string' && typeof bb == 'string') {
161
- const rs = aa.localeCompare(bb);
162
- if (rs == 0)
163
- continue;
164
- return rs * order;
165
- }
166
- }
167
- return -1;
168
- });
169
- this.#IdMap.clear();
170
- state.items.map((item, index) => this.#IdMap.set(item.id, index));
171
- }
172
- if (state.paging?.count) {
173
- const d = state.items.length - state.paging.count.current;
174
- state.paging.count.current = state.items.length;
175
- state.paging.count.total += d;
176
- }
177
- if (direction && stream.some(d => d.data?.paging)) {
178
- // Cache paging
179
- this.#pages.clear();
180
- stream.forEach(s => s.data?.paging && this.#pages.set(s.ref, s.data?.paging));
181
- // Caculate paging here
182
- const last_page_total = state.items.length;
183
- const total = stream.reduce((p, c) => p + (c.data?.paging?.count?.total || 0), 0);
184
- const prev = stream.reduce((p, c) => p + (c.data?.paging?.count?.prev || 0), 0);
185
- const next = stream.reduce((p, c) => p + (c.data?.paging?.count?.next || 0), 0);
186
- state.paging = {
187
- count: {
188
- current: state.items.length,
189
- next: next - (direction == 'backward' ? last_page_total : 0),
190
- prev: prev - (direction == 'forward' ? last_page_total : 0),
191
- total
192
- },
193
- has: {
194
- next: stream.some(s => s.data?.paging?.has?.next),
195
- prev: stream.some(s => s.data?.paging?.has?.prev)
196
- },
197
- page: {
198
- current: Math.min(...stream.map(s => s.data?.paging?.page?.current || 0)),
199
- total: Math.max(...stream.map(s => s.data?.paging?.page?.total || 0))
200
- }
201
- };
202
- }
203
- this.next(state);
204
- }
205
- fetch_data(options = {}, loading, flush = false) {
206
- if (flush) {
207
- this.#pages.clear();
208
- this.#queries.forEach(s => s.unsubscribe());
209
- this.#queries.clear();
210
- this.#IdMap.clear();
211
- }
212
- if (!this.ref)
213
- return;
214
- if (this.#refs.length == 0)
215
- return;
216
- if (this.getValue().loading && !flush)
217
- return;
218
- const remain_data_refs = this.#refs.filter(ref => {
219
- const paging = this.#pages.get(ref);
220
- if (!paging)
221
- return true;
222
- return loading == 'forward' ? paging.has?.next : paging.has?.prev;
223
- });
224
- if (remain_data_refs.length == 0)
225
- return;
226
- this.next({
227
- ...this.getValue(),
228
- items: flush ? [] : this.getValue().items,
229
- loading,
230
- options: {
231
- ...this.getValue().options || {},
232
- ...options
233
- }
234
- });
235
- this.collection_options.options = options;
236
- this.#sorters = Object.keys(options).filter(k => k.endsWith(':sort')).map(k => {
237
- const key = k.split(':sort')[0];
238
- const order = options[k] == 1 ? 1 : -1;
239
- return { key, order };
240
- });
241
- this.#sorters.every(a => a.key != 'id') && this.#sorters.push({ key: 'id', order: -1 });
242
- const queries = remain_data_refs.map((ref, index) => {
243
- const cursor = this.#pages.get(ref)?.cursor;
244
- const opts = {
245
- ...options,
246
- ...loading == 'backward' && cursor?.first ? { ':before': cursor?.first } : {},
247
- ...loading == 'forward' && cursor?.last ? { ':after': cursor?.last } : {},
248
- };
249
- return this
250
- .collection_options
251
- .transporter
252
- .query(ref, opts).pipe(map(data => ({
253
- ...data,
254
- ref
255
- })), share());
256
- });
257
- 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();
258
- 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();
259
- this.#queries.add(subscription);
260
- }
261
- reset() {
262
- this.fetch_data({}, 'both', true);
263
- }
264
- fetch_more() {
265
- const { options } = this.getValue();
266
- this.fetch_data(options, 'forward');
267
- }
268
- fetch_prev() {
269
- const { options } = this.getValue();
270
- this.fetch_data(options, 'backward');
271
- }
272
- filter(filters) {
273
- this.fetch_data(filters, 'forward', true);
274
- }
275
- #find_ref_by_id(id) {
276
- if (!id || !this.ref)
277
- return { ref: this.ref, collection_ref: this.ref, doc: null };
278
- const index = this.#IdMap.get(id);
279
- if (index == undefined)
280
- return {};
281
- const doc = this.getValue().items[index];
282
- const origin_ref = doc.__ref;
283
- if (!origin_ref)
284
- throw 'COLLECTION_REF_NOT_FOUND';
285
- const refs = origin_ref.split('/');
286
- const collection_ref = refs.slice(0, refs.length - (refs.length % 2 == 1 ? 0 : 1)).join('/');
287
- const ref = `${collection_ref}/${id}`;
288
- return { ref, id, collection_ref, index, doc };
289
- }
290
- async add(payload) {
291
- const r = await this.collection_options.transporter.add(`${this.ref}`, payload);
292
- if (r.data && r.data.item && !r.data.item.id) {
293
- r.data.item.id = r.data.item._id;
294
- }
295
- return r;
296
- }
297
- async update({ id: update_payload_id, ...payload }) {
298
- const { id, ref, doc } = this.#find_ref_by_id(update_payload_id);
299
- if (!ref)
300
- return;
301
- // Trigger local update
302
- this.#sync([{
303
- ref,
304
- data: {
305
- changes: [{
306
- data: { ...payload, id, __updating: true },
307
- ref,
308
- type: 'modified'
309
- }]
310
- }
311
- }], 'local');
312
- try {
313
- return await this.collection_options.transporter.update(ref, payload);
314
- }
315
- catch (e) {
316
- this.#sync([{
317
- ref,
318
- data: {
319
- changes: [{
320
- data: { ...doc || {}, id, __updating: false },
321
- ref,
322
- type: 'modified'
323
- }]
324
- }
325
- }], 'local');
326
- throw e;
327
- }
328
- }
329
- async remove(remove_document_id) {
330
- const { id, ref } = this.#find_ref_by_id(remove_document_id);
331
- if (!ref)
332
- return;
333
- this.#sync([{
334
- ref,
335
- data: {
336
- changes: [{
337
- data: { id, __removing: true },
338
- ref,
339
- type: 'modified'
340
- }]
341
- }
342
- }], 'local');
343
- // Trigger
344
- try {
345
- return await this.collection_options.transporter.remove(ref);
346
- }
347
- catch (e) {
348
- this.#sync([{
349
- ref,
350
- data: {
351
- changes: [{
352
- data: { id, __removing: false },
353
- ref,
354
- type: 'modified'
355
- }]
356
- }
357
- }], 'local');
358
- throw e;
359
- }
360
- }
361
- async trigger(name, payload, trigger_document_id, query = {}) {
362
- const { ref } = this.#find_ref_by_id(trigger_document_id);
363
- if (!ref)
364
- throw new Error('INVAILD_REF');
365
- return await this.collection_options.transporter.trigger(ref, name, payload, query);
366
- }
367
- }
package/build/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from './Collection.js';
package/build/index.js DELETED
@@ -1 +0,0 @@
1
- export * from './Collection.js';
@@ -1 +0,0 @@
1
- {"root":["../src/index.ts","../src/collection.ts"],"version":"5.6.3"}