@livequery/client 2.0.31 → 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 -366
  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.31",
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,366 +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.startsWith(':'))
81
- .every(key => {
82
- const [field, expression] = key.split(':');
83
- const a = payload[field];
84
- const b = state.options?.[field];
85
- const map = {
86
- eq: () => a == b,
87
- ne: () => a != b,
88
- lt: () => typeof a == 'number' && typeof b == 'number' && a < b,
89
- lte: () => typeof a == 'number' && typeof b == 'number' && a <= b,
90
- gt: () => typeof a == 'number' && typeof b == 'number' && a > b,
91
- gte: () => typeof a == 'number' && typeof b == 'number' && a >= b,
92
- in: () => Array.isArray(a) && a?.includes(b),
93
- like: () => typeof a == 'string' && a.includes(`${b}`),
94
- between: () => typeof a == 'number' && Array.isArray(b) && typeof b[0] == 'number' && b[0] <= a && typeof b[1] == 'number' && a <= b[1]
95
- };
96
- try {
97
- const expr = expression == '' ? 'eq' : expression;
98
- if (map[expr])
99
- return map[expr]();
100
- }
101
- catch (e) { }
102
- return true;
103
- }))) {
104
- const item = {
105
- ...payload,
106
- id: payload.id || payload._id,
107
- __adding: false,
108
- __updating: false,
109
- __removing: false,
110
- __remove: () => this.remove(payload?.id),
111
- __trigger: (name, input = undefined, query) => this.trigger(name, input, payload?.id, query),
112
- __update: (input) => this.update({ ...input, id: payload?.id }),
113
- __ref: change.ref,
114
- toJson: () => payload
115
- };
116
- direction == 'forward' ? state.items.push(item) : state.items.unshift(item);
117
- actions.reindex = true;
118
- }
119
- }
120
- if (index >= 0) {
121
- if (type == 'added' || type == 'modified') {
122
- const sort_key_value_updated = this.#sorters.some(({ key }) => {
123
- const value = payload[key];
124
- if (typeof value == 'string' || typeof value == 'number')
125
- return true;
126
- });
127
- if (sort_key_value_updated) {
128
- actions.reindex = true;
129
- }
130
- state.items[index] = {
131
- ...state.items[index],
132
- __adding: false,
133
- __updating: false,
134
- __removing: false,
135
- ...payload
136
- };
137
- }
138
- if (type == 'removed') {
139
- state.items.splice(index, 1);
140
- for (const [document_id, i] of this.#IdMap) {
141
- i == index && this.#IdMap.delete(document_id);
142
- i > index && this.#IdMap.set(document_id, i - 1);
143
- }
144
- }
145
- }
146
- }
147
- }
148
- if (actions.reindex) {
149
- state.items = state.items.sort((a, b) => {
150
- for (const { key, order } of this.#sorters) {
151
- const aa = a[key];
152
- const bb = b[key];
153
- if (typeof aa == 'number' && typeof bb == 'number') {
154
- const rs = aa - bb;
155
- if (rs == 0)
156
- continue;
157
- return rs * order;
158
- }
159
- if (typeof aa == 'string' && typeof bb == 'string') {
160
- const rs = aa.localeCompare(bb);
161
- if (rs == 0)
162
- continue;
163
- return rs * order;
164
- }
165
- }
166
- return -1;
167
- });
168
- this.#IdMap.clear();
169
- state.items.map((item, index) => this.#IdMap.set(item.id, index));
170
- }
171
- if (state.paging?.count) {
172
- const d = state.items.length - state.paging.count.current;
173
- state.paging.count.current = state.items.length;
174
- state.paging.count.total += d;
175
- }
176
- if (direction && stream.some(d => d.data?.paging)) {
177
- // Cache paging
178
- this.#pages.clear();
179
- stream.forEach(s => s.data?.paging && this.#pages.set(s.ref, s.data?.paging));
180
- // Caculate paging here
181
- const last_page_total = state.items.length;
182
- const total = stream.reduce((p, c) => p + (c.data?.paging?.count?.total || 0), 0);
183
- const prev = stream.reduce((p, c) => p + (c.data?.paging?.count?.prev || 0), 0);
184
- const next = stream.reduce((p, c) => p + (c.data?.paging?.count?.next || 0), 0);
185
- state.paging = {
186
- count: {
187
- current: state.items.length,
188
- next: next - (direction == 'backward' ? last_page_total : 0),
189
- prev: prev - (direction == 'forward' ? last_page_total : 0),
190
- total
191
- },
192
- has: {
193
- next: stream.some(s => s.data?.paging?.has?.next),
194
- prev: stream.some(s => s.data?.paging?.has?.prev)
195
- },
196
- page: {
197
- current: Math.min(...stream.map(s => s.data?.paging?.page?.current || 0)),
198
- total: Math.max(...stream.map(s => s.data?.paging?.page?.total || 0))
199
- }
200
- };
201
- }
202
- this.next(state);
203
- }
204
- fetch_data(options = {}, loading, flush = false) {
205
- if (flush) {
206
- this.#pages.clear();
207
- this.#queries.forEach(s => s.unsubscribe());
208
- this.#queries.clear();
209
- this.#IdMap.clear();
210
- }
211
- if (!this.ref)
212
- return;
213
- if (this.#refs.length == 0)
214
- return;
215
- if (this.getValue().loading && !flush)
216
- return;
217
- const remain_data_refs = this.#refs.filter(ref => {
218
- const paging = this.#pages.get(ref);
219
- if (!paging)
220
- return true;
221
- return loading == 'forward' ? paging.has?.next : paging.has?.prev;
222
- });
223
- if (remain_data_refs.length == 0)
224
- return;
225
- this.next({
226
- ...this.getValue(),
227
- items: flush ? [] : this.getValue().items,
228
- loading,
229
- options: {
230
- ...this.getValue().options || {},
231
- ...options
232
- }
233
- });
234
- this.collection_options.options = options;
235
- this.#sorters = Object.keys(options).filter(k => k.endsWith(':sort')).map(k => {
236
- const key = k.split(':sort')[0];
237
- const order = options[k] == 1 ? 1 : -1;
238
- return { key, order };
239
- });
240
- this.#sorters.every(a => a.key != 'id') && this.#sorters.push({ key: 'id', order: -1 });
241
- const queries = remain_data_refs.map((ref, index) => {
242
- const cursor = this.#pages.get(ref)?.cursor;
243
- const opts = {
244
- ...options,
245
- ...loading == 'backward' && cursor?.first ? { ':before': cursor?.first } : {},
246
- ...loading == 'forward' && cursor?.last ? { ':after': cursor?.last } : {},
247
- };
248
- return this
249
- .collection_options
250
- .transporter
251
- .query(ref, opts).pipe(map(data => ({
252
- ...data,
253
- ref
254
- })), share());
255
- });
256
- 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();
257
- 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();
258
- this.#queries.add(subscription);
259
- }
260
- reset() {
261
- this.fetch_data({}, 'both', true);
262
- }
263
- fetch_more() {
264
- const { options } = this.getValue();
265
- this.fetch_data(options, 'forward');
266
- }
267
- fetch_prev() {
268
- const { options } = this.getValue();
269
- this.fetch_data(options, 'backward');
270
- }
271
- filter(filters) {
272
- this.fetch_data(filters, 'forward', true);
273
- }
274
- #find_ref_by_id(id) {
275
- if (!id || !this.ref)
276
- return { ref: this.ref, collection_ref: this.ref, doc: null };
277
- const index = this.#IdMap.get(id);
278
- if (index == undefined)
279
- return {};
280
- const doc = this.getValue().items[index];
281
- const origin_ref = doc.__ref;
282
- if (!origin_ref)
283
- throw 'COLLECTION_REF_NOT_FOUND';
284
- const refs = origin_ref.split('/');
285
- const collection_ref = refs.slice(0, refs.length - (refs.length % 2 == 1 ? 0 : 1)).join('/');
286
- const ref = `${collection_ref}/${id}`;
287
- return { ref, id, collection_ref, index, doc };
288
- }
289
- async add(payload) {
290
- const r = await this.collection_options.transporter.add(`${this.ref}`, payload);
291
- if (r.data && r.data.item && !r.data.item.id) {
292
- r.data.item.id = r.data.item._id;
293
- }
294
- return r;
295
- }
296
- async update({ id: update_payload_id, ...payload }) {
297
- const { id, ref, doc } = this.#find_ref_by_id(update_payload_id);
298
- if (!ref)
299
- return;
300
- // Trigger local update
301
- this.#sync([{
302
- ref,
303
- data: {
304
- changes: [{
305
- data: { ...payload, id, __updating: true },
306
- ref,
307
- type: 'modified'
308
- }]
309
- }
310
- }], 'local');
311
- try {
312
- return await this.collection_options.transporter.update(ref, payload);
313
- }
314
- catch (e) {
315
- this.#sync([{
316
- ref,
317
- data: {
318
- changes: [{
319
- data: { ...doc || {}, id, __updating: false },
320
- ref,
321
- type: 'modified'
322
- }]
323
- }
324
- }], 'local');
325
- throw e;
326
- }
327
- }
328
- async remove(remove_document_id) {
329
- const { id, ref } = this.#find_ref_by_id(remove_document_id);
330
- if (!ref)
331
- return;
332
- this.#sync([{
333
- ref,
334
- data: {
335
- changes: [{
336
- data: { id, __removing: true },
337
- ref,
338
- type: 'modified'
339
- }]
340
- }
341
- }], 'local');
342
- // Trigger
343
- try {
344
- return await this.collection_options.transporter.remove(ref);
345
- }
346
- catch (e) {
347
- this.#sync([{
348
- ref,
349
- data: {
350
- changes: [{
351
- data: { id, __removing: false },
352
- ref,
353
- type: 'modified'
354
- }]
355
- }
356
- }], 'local');
357
- throw e;
358
- }
359
- }
360
- async trigger(name, payload, trigger_document_id, query = {}) {
361
- const { ref } = this.#find_ref_by_id(trigger_document_id);
362
- if (!ref)
363
- throw new Error('INVAILD_REF');
364
- return await this.collection_options.transporter.trigger(ref, name, payload, query);
365
- }
366
- }
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"}