@livequery/mongodb 2.0.145 → 2.0.148

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.
package/README.md CHANGED
@@ -314,33 +314,46 @@ MongoDB change stream watcher for realtime Livequery updates. This replaces the
314
314
  import { WebsocketGateway } from '@livequery/core'
315
315
  import { MongoDatasource, MongodbRealtime } from '@livequery/mongodb'
316
316
 
317
- const routes = [
317
+ const datasource = new MongoDatasource({
318
+ connections: { default: client },
319
+ databases: ['main'],
320
+ })
321
+
322
+ await datasource.init([
318
323
  {
319
324
  method: 'GET',
320
325
  path: '/products',
321
326
  collection: 'products',
322
327
  realtime: true,
323
328
  },
324
- ]
325
-
326
- const datasource = new MongoDatasource({
327
- connections: { default: client },
328
- databases: ['main'],
329
- })
330
-
331
- await datasource.init(routes)
329
+ ])
332
330
 
333
331
  const websocketGateway = new WebsocketGateway(server)
334
332
 
335
333
  new MongodbRealtime()
336
- .watch(datasource.config, routes)
334
+ .watch(datasource.config, [
335
+ {
336
+ // LivequeryRequestParser.parse(...).schema — document-id segment already stripped
337
+ schema: 'products',
338
+ options: { collection: 'products', realtime: true },
339
+ },
340
+ ])
337
341
  .subscribe(websocketGateway)
338
342
  ```
339
343
 
344
+ `MongoRealtimeRoute`:
345
+
346
+ ```ts
347
+ type MongoRealtimeRoute = {
348
+ schema: string // parsed route path from @livequery/core, e.g. 'users/:userId/posts'
349
+ options: RouteOptions
350
+ }
351
+ ```
352
+
340
353
  Realtime route requirements:
341
354
 
342
- - `method` must be `GET` or legacy method `0`.
343
355
  - `realtime` must be `true`.
356
+ - `schema` is the parsed route path (`LivequeryRequestParser.parse(...).schema`), so the document-id segment is already stripped and each `:param` names the document field holding the parent value.
344
357
  - `collection` must be a static string. Dynamic collection, database, or connection resolver functions are skipped because database watchers must be known up front.
345
358
  - When watching a `MongoClient`, `db` or `config.databases` decides which database names to watch. When watching a `Db`, that database is used directly.
346
359
 
@@ -352,20 +365,17 @@ Disable the `collMod` call when your deployment manages pre/post images separate
352
365
  new MongodbRealtime({ enablePreAndPostImages: false })
353
366
  ```
354
367
 
355
- For nested collection refs, use `refFields` to map route params to document fields:
368
+ For nested collection refs, name the route param after the document field that holds the parent value:
356
369
 
357
370
  ```ts
358
371
  {
359
- method: 'GET',
360
- path: '/users/:id/posts',
361
- collection: 'posts',
362
- realtime: true,
363
- refFields: {
364
- id: 'userId',
365
- },
372
+ schema: 'users/:userId/posts',
373
+ options: { collection: 'posts', realtime: true },
366
374
  }
367
375
  ```
368
376
 
377
+ If the document field is an array (one document belongs to many parents), the change is fanned out to one ref per array element, and array membership changes emit `added`/`removed` per ref.
378
+
369
379
  An inserted `{ _id: 'post1', userId: 'user1', title: 'Hello' }` emits:
370
380
 
371
381
  ```ts
@@ -425,7 +435,6 @@ type RouteOptions = {
425
435
  collection: string | ((req: LivequeryRequest) => Promise<string> | string)
426
436
  db?: string | ((req: LivequeryRequest) => Promise<string> | string)
427
437
  connection?: string | ((req: LivequeryRequest) => Promise<string> | string)
428
- refFields?: Record<string, string | { field: string, array?: boolean }>
429
438
  objectIdFields?: string[]
430
439
  }
431
440
  ```
@@ -436,7 +445,6 @@ Fields:
436
445
  - `collection`: required collection name or resolver function.
437
446
  - `db`: optional database name or resolver function.
438
447
  - `connection`: optional connection name or resolver function.
439
- - `refFields`: optional mapping from nested route params to document fields for realtime ref formatting.
440
448
  - `objectIdFields`: top-level request fields that should be converted from valid string ids to `ObjectId`.
441
449
 
442
450
  Use function values when tenant, database, or collection depends on request keys.
@@ -1,5 +1,4 @@
1
- import type { LivequeryBaseEntity, QueryOption } from "./types.js";
2
1
  export declare class Cursor {
3
- static caculate<T extends LivequeryBaseEntity>(item: T, options: QueryOption<T>): string;
4
- static parse<T extends LivequeryBaseEntity>(cursor: string): any;
2
+ static caculate(item: Record<string, any>, options: Record<string, any>): string;
3
+ static parse(cursor: string): any;
5
4
  }
@@ -1,15 +1,7 @@
1
1
  import type { LivequeryContext, LivequeryDatasource as CoreLivequeryDatasource, LivequeryDatasourceInitConfig } from '@livequery/core';
2
- import type { LivequeryRequest, LivequeryBaseEntity, WebsocketSyncPayload } from './types.js';
2
+ import type { LivequeryRequest, LivequeryBaseEntity, UpdatedData } from '@livequery/core';
3
3
  import type { Db, MongoClient } from 'mongodb';
4
4
  import { Subject } from 'rxjs';
5
- export type LivequeryDatasource<Config, RouteOptions> = Subject<WebsocketSyncPayload<LivequeryBaseEntity>> & {
6
- init(config: Config, routes: Array<{
7
- path: string;
8
- method: number;
9
- options: RouteOptions;
10
- }>): Promise<void>;
11
- query: (query: LivequeryRequest, options: RouteOptions) => Promise<any>;
12
- };
13
5
  export type MongoConnection = MongoClient | Db;
14
6
  export type MongoDatasourceConfig = {
15
7
  connections: {
@@ -17,31 +9,20 @@ export type MongoDatasourceConfig = {
17
9
  };
18
10
  databases?: string[];
19
11
  };
20
- type LegacyRouteConfig<RouteOptions> = {
21
- path: string;
22
- method: number | string;
23
- options?: RouteOptions;
24
- config?: RouteOptions;
25
- };
26
12
  export type RouteOptions = {
27
13
  realtime?: boolean;
28
14
  collection: string | ((req: LivequeryRequest) => Promise<string> | string);
29
15
  db?: string | ((req: LivequeryRequest) => Promise<string> | string);
30
16
  connection?: string | ((req: LivequeryRequest) => Promise<string> | string);
31
- refFields?: Record<string, string | {
32
- field: string;
33
- array?: boolean;
34
- }>;
35
17
  objectIdFields?: string[];
36
18
  };
37
- export declare class MongoDatasource extends Subject<WebsocketSyncPayload<LivequeryBaseEntity>> implements LivequeryDatasource<MongoDatasourceConfig, RouteOptions>, CoreLivequeryDatasource<RouteOptions> {
19
+ export declare class MongoDatasource extends Subject<UpdatedData<LivequeryBaseEntity>> implements CoreLivequeryDatasource<RouteOptions> {
38
20
  #private;
39
21
  readonly refs: Map<string, Set<string>>;
40
22
  config: MongoDatasourceConfig;
41
23
  routes: Map<string, RouteOptions>;
42
24
  constructor(config?: MongoDatasourceConfig);
43
25
  init(routes: Array<LivequeryDatasourceInitConfig<RouteOptions>>): Promise<void>;
44
- init(config: MongoDatasourceConfig, routes: Array<LegacyRouteConfig<RouteOptions>>): Promise<void>;
45
26
  handle(ctx: LivequeryContext): Promise<{}>;
46
27
  query(req: LivequeryRequest, options: RouteOptions): Promise<{
47
28
  items: any[];
@@ -68,4 +49,3 @@ export declare class MongoDatasource extends Subject<WebsocketSyncPayload<Livequ
68
49
  item: any;
69
50
  }>;
70
51
  }
71
- export {};
@@ -1,6 +1,6 @@
1
1
  import { Cursor } from './Cursor.js';
2
2
  import { MongoQuery } from "./MongoQuery.js";
3
- import { ObjectId } from 'bson';
3
+ import { ObjectId } from 'mongodb';
4
4
  import { SmartCache } from './SmartCache.js';
5
5
  import { Subject } from 'rxjs';
6
6
  export class MongoDatasource extends Subject {
@@ -14,23 +14,18 @@ export class MongoDatasource extends Subject {
14
14
  this.config = config;
15
15
  this.routes = new Map();
16
16
  }
17
- async init(configOrRoutes, maybeRoutes) {
18
- const routes = Array.isArray(configOrRoutes)
19
- ? configOrRoutes
20
- : maybeRoutes || [];
21
- if (!Array.isArray(configOrRoutes)) {
22
- this.config = configOrRoutes;
23
- }
17
+ async init(routes) {
24
18
  this.routes = routes.reduce((p, c) => {
25
- const options = this.#getRouteOptions(c);
26
- if (!options.collection)
19
+ const { method, path, ...options } = c;
20
+ const routeOptions = options;
21
+ if (!routeOptions.collection)
27
22
  return p;
28
23
  const key = this.#routeKey(c.method, c.path);
29
24
  const set = p.get(key) || p.get(c.path);
30
- if (set && set.collection != options.collection)
25
+ if (set && set.collection != routeOptions.collection)
31
26
  throw new Error('Collection mismatch for route path "' + c.path + '"');
32
- p.set(key, options);
33
- p.set(c.path, options);
27
+ p.set(key, routeOptions);
28
+ p.set(c.path, routeOptions);
34
29
  return p;
35
30
  }, new Map());
36
31
  }
@@ -59,15 +54,6 @@ export class MongoDatasource extends Subject {
59
54
  return this.#del(query, collection);
60
55
  throw { status: 500, code: 'INVAILD_METHOD', message: 'Invaild method' };
61
56
  }
62
- #getRouteOptions(route) {
63
- const legacy = route;
64
- if (legacy.options)
65
- return legacy.options;
66
- if (legacy.config)
67
- return legacy.config;
68
- const { method, path, ...options } = route;
69
- return options;
70
- }
71
57
  #getOptions(ctx) {
72
58
  const routePath = ctx.request.ref || ctx.request.path;
73
59
  const options = this.routes.get(this.#routeKey(ctx.request.method, routePath)) || this.routes.get(routePath);
@@ -87,23 +73,20 @@ export class MongoDatasource extends Subject {
87
73
  is_collection: !livequery.document_id,
88
74
  collection_ref: livequery.collection_ref,
89
75
  schema_collection_ref: livequery.schema_collection_ref,
90
- doc_id: livequery.document_id,
76
+ document_id: livequery.document_id,
91
77
  keys: livequery.keys || {},
92
78
  query: livequery.query || {},
93
- options: livequery.query || {},
94
79
  method: livequery.method?.toLowerCase(),
95
80
  body: livequery.body,
96
81
  };
97
82
  }
98
83
  #normalizeRequest(req) {
99
- const options = req.options || req.query || {};
84
+ const query = req.query || {};
100
85
  return {
101
86
  ...req,
102
87
  keys: req.keys || {},
103
- query: req.query || options,
104
- options,
88
+ query,
105
89
  is_collection: typeof req.is_collection == 'boolean' ? req.is_collection : !req.document_id,
106
- doc_id: req.doc_id || req.document_id,
107
90
  method: req.method?.toLowerCase(),
108
91
  };
109
92
  }
@@ -128,8 +111,8 @@ export class MongoDatasource extends Subject {
128
111
  const total = current + count.next + count.prev;
129
112
  const paging = {
130
113
  cursor: {
131
- last: Cursor.caculate(items[items.length - 1], req.options),
132
- first: Cursor.caculate(items[0], req.options)
114
+ last: Cursor.caculate(items[items.length - 1], req.query),
115
+ first: Cursor.caculate(items[0], req.query)
133
116
  },
134
117
  has,
135
118
  count: {
@@ -201,7 +184,7 @@ export class MongoDatasource extends Subject {
201
184
  const keys = req.keys || {};
202
185
  const isPlainBody = req.body && typeof req.body === 'object'
203
186
  && !Object.keys(req.body).some(k => k.startsWith('$'));
204
- const id = keys.id ?? req.doc_id;
187
+ const id = keys.id ?? req.document_id;
205
188
  return {
206
189
  ...keys,
207
190
  ...isPlainBody ? req.body : {},
@@ -213,13 +196,19 @@ export class MongoDatasource extends Subject {
213
196
  return {
214
197
  ...p,
215
198
  ...k == 'id' ? {
216
- _id: ObjectId.createFromHexString(req.keys.id)
199
+ _id: this.#objectId('id', req.keys.id)
217
200
  } : {
218
201
  [k]: c
219
202
  }
220
203
  };
221
204
  }, {});
222
205
  }
206
+ #objectId(field, value) {
207
+ if (typeof value != 'string' || !ObjectId.isValid(value)) {
208
+ throw { status: 400, code: 'INVALID_OBJECT_ID', message: `Invalid ObjectId for field "${field}": ${JSON.stringify(value)}` };
209
+ }
210
+ return ObjectId.createFromHexString(value);
211
+ }
223
212
  #update(body) {
224
213
  if (!body || Object.keys(body).some(key => key.startsWith('$')))
225
214
  return body;
@@ -1,4 +1,4 @@
1
- import type { LivequeryBaseEntity, LivequeryRequest } from "./types.js";
1
+ import type { LivequeryBaseEntity, LivequeryRequest } from "@livequery/core";
2
2
  import type { Collection } from "mongodb";
3
3
  export declare class MongoQuery {
4
4
  #private;
@@ -1,5 +1,5 @@
1
1
  import { Cursor } from "./Cursor.js";
2
- import { ObjectId } from "bson";
2
+ import { ObjectId } from "mongodb";
3
3
  export class MongoQuery {
4
4
  static #is_operator(c) {
5
5
  return ['+', '-', '*', '/', '(', ')', '~'].indexOf(c) !== -1;
@@ -94,6 +94,20 @@ export class MongoQuery {
94
94
  static #escape_regex(value) {
95
95
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
96
96
  }
97
+ static #objectId(field, value) {
98
+ if (typeof value != 'string' || !ObjectId.isValid(value)) {
99
+ throw { status: 400, code: 'INVALID_OBJECT_ID', message: `Invalid ObjectId for field "${field}": ${JSON.stringify(value)}` };
100
+ }
101
+ return ObjectId.createFromHexString(value);
102
+ }
103
+ static #parse_cursor(token) {
104
+ try {
105
+ return Cursor.parse(token);
106
+ }
107
+ catch {
108
+ throw { status: 400, code: 'INVALID_CURSOR', message: 'Invalid pagination cursor' };
109
+ }
110
+ }
97
111
  static #parse_array(value) {
98
112
  if (Array.isArray(value))
99
113
  return value;
@@ -109,7 +123,7 @@ export class MongoQuery {
109
123
  }
110
124
  static #parse_summary(req) {
111
125
  const parsed = Object
112
- .entries(req.options)
126
+ .entries(req.query)
113
127
  .map(([key, v], index) => {
114
128
  if (!key.startsWith('::'))
115
129
  return [];
@@ -278,11 +292,11 @@ export class MongoQuery {
278
292
  return { $and: clauses };
279
293
  }
280
294
  static #build_search_query(req) {
281
- const search = req.options[":search"];
295
+ const search = req.query[":search"];
282
296
  return search ? [{ $match: { $text: { $search: `${search}` } } }] : [];
283
297
  }
284
298
  static #get_limit(req) {
285
- const l = Number(req.options[':limit']);
299
+ const l = Number(req.query[':limit']);
286
300
  if (isNaN(l))
287
301
  return 10;
288
302
  if (l < 1)
@@ -300,13 +314,19 @@ export class MongoQuery {
300
314
  }
301
315
  ];
302
316
  }
317
+ static #topn_sorter($sort) {
318
+ return Object.entries($sort).reduce((p, [key, order]) => ({
319
+ ...p,
320
+ [key == '_id' ? 'id' : key]: order
321
+ }), {});
322
+ }
303
323
  static #build_cursor_query($sort, req, reverse = false) {
304
324
  const limit = this.#get_limit(req);
305
- const after = req.options[':after'];
306
- const before = req.options[':before'];
307
- const around = req.options[':around'];
325
+ const after = req.query[':after'];
326
+ const before = req.query[':before'];
327
+ const around = req.query[':around'];
308
328
  const pagination_token = around || before || after;
309
- const cursor = pagination_token ? Cursor.parse(pagination_token) : (reverse ? null : {});
329
+ const cursor = pagination_token ? this.#parse_cursor(pagination_token) : (reverse ? null : {});
310
330
  if (!cursor)
311
331
  return [{ $limit: 1 }, { $match: { _id: 0 } }];
312
332
  const $or = Object.entries({ ...$sort, _id: $sort._id || -1 }).map(([key, order], index, arr) => {
@@ -341,7 +361,7 @@ export class MongoQuery {
341
361
  items: {
342
362
  [reverse ? '$bottomN' : '$topN']: {
343
363
  n: limit,
344
- sortBy: {},
364
+ sortBy: this.#topn_sorter($sort),
345
365
  output: "$$ROOT"
346
366
  }
347
367
  }
@@ -360,7 +380,7 @@ export class MongoQuery {
360
380
  ];
361
381
  }
362
382
  static #build_cursor_paging($sort, req) {
363
- if (req.options[':after'] || req.options[':before'] || req.options[':around']) {
383
+ if (req.query[':after'] || req.query[':before'] || req.query[':around']) {
364
384
  }
365
385
  const { pipelines, summary } = this.#parse_summary(req);
366
386
  const limit = this.#get_limit(req);
@@ -409,7 +429,7 @@ export class MongoQuery {
409
429
  }
410
430
  static #build_offset_paging(req) {
411
431
  const limit = this.#get_limit(req);
412
- const page = Math.max(1, Math.floor(Number(req.options[':page'])) || 1);
432
+ const page = Math.max(1, Math.floor(Number(req.query[':page'])) || 1);
413
433
  const skip = (page - 1) * limit;
414
434
  const { pipelines, summary } = this.#parse_summary(req);
415
435
  return [
@@ -444,12 +464,12 @@ export class MongoQuery {
444
464
  ];
445
465
  }
446
466
  static #build_query_filter(req) {
447
- const { ":after": after, ":before": before, ':around': around, ":limit": _limit, ":page": _page, ":search": search, ...rest } = req.options;
467
+ const { ":after": after, ":before": before, ':around': around, ":limit": _limit, ":page": _page, ":search": search, ...rest } = req.query;
448
468
  return this.#parse_conditions({ ...rest, ...req.keys });
449
469
  }
450
470
  static #get_sorter(req) {
451
471
  let default_sort = -1;
452
- const $sort = Object.entries(req.options).reduce((p, [k, order]) => {
472
+ const $sort = Object.entries(req.query).reduce((p, [k, order]) => {
453
473
  if (!k.endsWith(':sort'))
454
474
  return p;
455
475
  const by = k.split(':sort')[0];
@@ -474,7 +494,7 @@ export class MongoQuery {
474
494
  {
475
495
  $match: {
476
496
  ...req.keys,
477
- ...req.keys.id ? { id: undefined, _id: ObjectId.createFromHexString(req.keys.id) } : {}
497
+ ...req.keys.id ? { id: undefined, _id: this.#objectId('id', req.keys.id) } : {}
478
498
  }
479
499
  },
480
500
  ...this.#rename_id(),
@@ -493,7 +513,7 @@ export class MongoQuery {
493
513
  summary: {}
494
514
  };
495
515
  }
496
- const is_cursor_paging = req.options[':after'] || req.options[':before'] || req.options[':around'] || !req.options[':page'];
516
+ const is_cursor_paging = req.query[':after'] || req.query[':before'] || req.query[':around'] || !req.query[':page'];
497
517
  const $sort = this.#get_sorter(req);
498
518
  const pipelines = [
499
519
  { $sort },
@@ -1,23 +1,17 @@
1
1
  import { Observable } from 'rxjs';
2
- import type { WebsocketSyncPayload } from './types.js';
2
+ import type { UpdatedData } from '@livequery/core';
3
3
  import type { MongoDatasourceConfig, RouteOptions } from './MongoDatasource.js';
4
4
  export type MongoRealtimeChangeType = 'added' | 'modified' | 'removed';
5
- export type MongoRealtimeRefField = string | {
6
- field: string;
7
- array?: boolean;
8
- };
9
5
  export type MongoRealtimeOptions = {
10
6
  enablePreAndPostImages?: boolean;
11
7
  };
12
8
  export type MongoRealtimeRoute = {
13
- path: string;
14
- method: string | number;
15
- options?: RouteOptions;
16
- config?: RouteOptions;
17
- } & Partial<RouteOptions>;
9
+ schema: string;
10
+ options: RouteOptions;
11
+ };
18
12
  export declare class MongodbRealtime {
19
13
  #private;
20
14
  private options;
21
15
  constructor(options?: MongoRealtimeOptions);
22
- watch(config: MongoDatasourceConfig, routes: MongoRealtimeRoute[]): Observable<WebsocketSyncPayload>;
16
+ watch(config: MongoDatasourceConfig, routes: MongoRealtimeRoute[]): Observable<UpdatedData<any>>;
23
17
  }
@@ -23,17 +23,6 @@ export class MongodbRealtime {
23
23
  }, {}),
24
24
  };
25
25
  }
26
- #isGetRoute(method) {
27
- return method === 0 || String(method).toUpperCase() === 'GET';
28
- }
29
- #getRouteOptions(route) {
30
- if (route.options)
31
- return route.options;
32
- if (route.config)
33
- return route.config;
34
- const { method: _method, path: _path, options: _options, config: _config, ...options } = route;
35
- return options.collection ? options : undefined;
36
- }
37
26
  #isDb(connection) {
38
27
  return typeof connection.collection == 'function';
39
28
  }
@@ -45,8 +34,8 @@ export class MongodbRealtime {
45
34
  #watchSources(config, routes) {
46
35
  const sources = new Map();
47
36
  for (const route of routes) {
48
- const options = this.#getRouteOptions(route);
49
- if (!options?.realtime || !this.#isGetRoute(route.method))
37
+ const options = route.options;
38
+ if (!options?.realtime)
50
39
  continue;
51
40
  if (typeof options.collection != 'string')
52
41
  continue;
@@ -99,7 +88,7 @@ export class MongodbRealtime {
99
88
  table: change.ns.coll,
100
89
  type,
101
90
  new_data: this.#reformatId(change.fullDocument),
102
- old_data: this.#reformatId(change.fullDocumentBeforeChange),
91
+ old_data: this.#reformatId(change.fullDocumentBeforeChange ?? change.documentKey),
103
92
  fields,
104
93
  });
105
94
  });
@@ -110,35 +99,31 @@ export class MongodbRealtime {
110
99
  }), mergeMap(stream => stream), retry());
111
100
  }
112
101
  #routeRefMetadata(route) {
113
- const options = this.#getRouteOptions(route);
114
- if (!options?.realtime || !this.#isGetRoute(route.method))
102
+ const options = route.options;
103
+ if (!options?.realtime)
115
104
  return;
116
105
  if (typeof options.collection != 'string')
117
106
  return;
118
- const segments = route.path.split('/').filter(Boolean);
119
- const collectionSegments = segments.length % 2 === 0 ? segments.slice(0, -1) : segments;
120
- const ref = collectionSegments.map(segment => segment.startsWith(':') ? '' : segment).filter(Boolean).join('/');
107
+ const segments = route.schema.split('/').filter(Boolean);
108
+ const ref = segments.filter(segment => !segment.startsWith(':')).join('/');
121
109
  if (!ref)
122
110
  return;
123
- const metadata = collectionSegments
111
+ const metadata = segments
124
112
  .map((collection, index) => {
125
113
  if (index % 2 === 1)
126
114
  return [];
127
- const param = collectionSegments[index + 1];
115
+ const param = segments[index + 1];
128
116
  if (!param?.startsWith(':'))
129
117
  return [{ collection }];
130
- const paramName = param.slice(1);
131
- const mapped = options.refFields?.[paramName] || paramName;
132
- const field = typeof mapped == 'string' ? mapped : mapped.field;
133
- const array = typeof mapped == 'string' ? undefined : mapped.array;
134
- return [{ collection, field: field == 'id' ? '_id' : field, array }];
118
+ const field = param.slice(1);
119
+ return [{ collection, field: field == 'id' ? '_id' : field }];
135
120
  })
136
121
  .flat();
137
122
  return [ref, metadata];
138
123
  }
139
124
  #paths(routes) {
140
125
  return routes.reduce((paths, route) => {
141
- const options = this.#getRouteOptions(route);
126
+ const options = route.options;
142
127
  const entry = this.#routeRefMetadata(route);
143
128
  if (!options || !entry || typeof options.collection != 'string')
144
129
  return paths;
@@ -179,16 +164,16 @@ export class MongodbRealtime {
179
164
  return 'removed';
180
165
  return 'modified';
181
166
  };
182
- const buildRefs = ([{ array, collection, field }, ...fields]) => {
167
+ const buildRefs = ([{ collection, field }, ...fields]) => {
183
168
  if (fields.length === 0 || !field)
184
169
  return [{ refs: [collection], type: event.type }];
185
- const values = array
186
- ? Array.isArray(merged[field])
187
- ? [...new Set([
188
- ...(event.old_data?.[field]?.map((item) => String(item)) || []),
189
- ...(event.new_data?.[field]?.map((item) => String(item)) || []),
190
- ])]
191
- : ['-']
170
+ const oldValues = event.old_data?.[field];
171
+ const newValues = event.new_data?.[field];
172
+ const values = Array.isArray(oldValues) || Array.isArray(newValues)
173
+ ? [...new Set([
174
+ ...(Array.isArray(oldValues) ? oldValues : []).map((item) => String(item)),
175
+ ...(Array.isArray(newValues) ? newValues : []).map((item) => String(item)),
176
+ ])]
192
177
  : [merged[field] ?? '-'];
193
178
  return values.flatMap(value => {
194
179
  return buildRefs(fields).map(next => ({
@@ -1,4 +1,3 @@
1
1
  export * from './MongoDatasource.js';
2
2
  export * from './DataChangePayload.js';
3
3
  export * from './MongodbRealtime.js';
4
- export * from './types.js';
@@ -1,4 +1,3 @@
1
1
  export * from './MongoDatasource.js';
2
2
  export * from './DataChangePayload.js';
3
3
  export * from './MongodbRealtime.js';
4
- export * from './types.js';
@@ -1 +1 @@
1
- {"root":["../src/cursor.ts","../src/datachangepayload.ts","../src/mongodatasource.ts","../src/mongoquery.ts","../src/mongodbrealtime.ts","../src/smartcache.ts","../src/index.ts","../src/types.ts"],"version":"5.9.3"}
1
+ {"root":["../src/cursor.ts","../src/datachangepayload.ts","../src/mongodatasource.ts","../src/mongoquery.ts","../src/mongodbrealtime.ts","../src/smartcache.ts","../src/index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "repository": {
7
7
  "url": "git@github.com:livequery/mongodb.git"
8
8
  },
9
- "version": "2.0.145",
9
+ "version": "2.0.148",
10
10
  "description": "MongoDB datasource mapping for @livequery ecosystem",
11
11
  "main": "./build/src/index.js",
12
12
  "types": "./build/src/index.d.ts",
@@ -34,7 +34,6 @@
34
34
  "deploy": "rm -rf build && yarn build; git add .; git commit -m \"Update\"; git push origin master; npm publish --access public"
35
35
  },
36
36
  "peerDependencies": {
37
- "bson": "*",
38
37
  "mongodb": "^6.20.0"
39
38
  }
40
39
  }
@@ -1,46 +0,0 @@
1
- import type { LivequeryRequest as CoreLivequeryRequest } from '@livequery/core';
2
- export type LivequeryBaseEntity = {
3
- id?: string;
4
- [key: string]: any;
5
- };
6
- export type QueryOption<T extends LivequeryBaseEntity = LivequeryBaseEntity> = Record<string, any>;
7
- export type FilterConditions<T extends LivequeryBaseEntity = LivequeryBaseEntity> = Record<string, any>;
8
- export type Paging = {
9
- cursor: {
10
- last: string | null;
11
- first: string | null;
12
- };
13
- has: {
14
- prev: boolean;
15
- next: boolean;
16
- };
17
- count: {
18
- prev: number;
19
- next: number;
20
- current: number;
21
- total: number;
22
- };
23
- page: {
24
- current: number;
25
- total: number;
26
- };
27
- };
28
- export type WebsocketSyncPayload<T extends LivequeryBaseEntity = LivequeryBaseEntity> = {
29
- ref?: string;
30
- type?: string;
31
- data?: T;
32
- [key: string]: any;
33
- };
34
- export type LivequeryRequest<T = any> = Partial<CoreLivequeryRequest<T>> & {
35
- keys: Record<string, any>;
36
- ref: string;
37
- collection_ref?: string;
38
- schema_collection_ref?: string;
39
- method: string;
40
- body?: T;
41
- query?: Record<string, any>;
42
- options: Record<string, any>;
43
- is_collection: boolean;
44
- doc_id?: string;
45
- document_id?: string;
46
- };
@@ -1 +0,0 @@
1
- export {};