@omegup/msync 0.0.47 → 0.0.49

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 (4) hide show
  1. package/index.d.ts +19 -10
  2. package/index.esm.js +45 -28
  3. package/index.js +46 -27
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -27,7 +27,6 @@ declare const Predicate: unique symbol;
27
27
  interface Predicate<in V> {
28
28
  [Type]?(x: typeof Predicate, _: V): void;
29
29
  raw: PredicateRaw;
30
- expr: <D extends O, C>(field: Field<D, V, C>) => Expr<boolean, D, C>;
31
30
  }
32
31
 
33
32
  declare class Field<in R, out V, in C = unknown> {
@@ -629,14 +628,24 @@ declare const wrap: <Result>(root: Machine<Result>) => Machine<Result>;
629
628
  declare const $eq: <T extends unknown>(operand: rawItem & T) => Predicate<T>;
630
629
  declare const $ne: <T extends unknown>(operand: rawItem & T) => Predicate<T>;
631
630
  type Numeric = number | Timestamp | Date;
632
- declare const comp: <D2 extends Numeric = number>(op: "$lte" | "$gte" | "$lt" | "$gt") => <T extends D2>(operand: rawItem & D2) => Predicate<D2>;
633
- declare const $gt: <T extends number>(operand: rawItem & number) => Predicate<number>;
634
- declare const $gtTs: <T extends Timestamp>(operand: rawItem & Timestamp) => Predicate<Timestamp>;
635
- declare const $gteTs: <T extends Timestamp>(operand: rawItem & Timestamp) => Predicate<Timestamp>;
636
- declare const $lt: <T extends number>(operand: rawItem & number) => Predicate<number>;
637
- declare const dateLt: <T extends Date>(operand: rawItem & Date) => Predicate<Date>;
638
- declare const $gte: <T extends number>(operand: rawItem & number) => Predicate<number>;
639
- declare const $lte: <T extends number>(operand: rawItem & number) => Predicate<number>;
631
+ declare const comp: <D2 extends Numeric = Numeric>(op: "$lte" | "$gte" | "$lt" | "$gt") => <T extends Numeric>(operand: rawItem & D2) => Predicate<D2>;
632
+ declare const $gt: <T extends Numeric>(operand: rawItem & Numeric) => Predicate<Numeric>;
633
+ declare const $gtTs: <T extends Numeric>(operand: rawItem & Timestamp) => Predicate<Timestamp>;
634
+ declare const $gteTs: <T extends Numeric>(operand: rawItem & Timestamp) => Predicate<Timestamp>;
635
+ declare const $lt: <T extends Numeric>(operand: rawItem & number) => Predicate<number>;
636
+ declare const dateLt: <T extends Numeric>(operand: rawItem & Date) => Predicate<Date>;
637
+ declare const $gte: <T extends Numeric>(operand: rawItem & number) => Predicate<number>;
638
+ declare const $lte: <T extends Numeric>(operand: rawItem & number) => Predicate<number>;
639
+ type MongoTypes = {
640
+ number: number;
641
+ array: readonly rawItem[];
642
+ string: string;
643
+ object: RawObj;
644
+ [i: number]: unknown;
645
+ };
646
+ type MongoTypeNames = keyof MongoTypes;
647
+ declare const $type: <T extends keyof MongoTypes>(operand: rawItem & readonly T[]) => Predicate<N | MongoTypes[T]>;
648
+ declare const $exists: <T extends keyof MongoTypes>(operand: rawItem & boolean) => Predicate<unknown>;
640
649
 
641
650
  declare const $expr: <D extends O, C>(expr: Expr<boolean, D, C>) => Query<D, C>;
642
651
 
@@ -667,4 +676,4 @@ declare const enablePreAndPostImages: <T extends doc>(coll: Collection<T>) => Pr
667
676
  declare const prepare: (testName?: string) => Promise<MongoClient$1>;
668
677
  declare const makeCol: <T extends ID>(docs: readonly OptionalUnlessRequiredId<T>[], database: Db, name?: string) => Promise<Collection<T>>;
669
678
 
670
- export { $accumulator, $and, $countDict, $entries, $eq, $expr, $getField, $group, $groupId, $groupMerge, $gt, $gtTs, $gte, $gteTs, $ifNull, $in, $insert, $insertX, $keys, $let, $lookup, $lt, $lte, $map, $map1, $match, $matchDelta, $merge, $merge_, $ne, $nin, $nor, $or, $outerLookup, $pushDict, $rand, $replaceWith, $set, $simpleInsert, $simpleMerge, $sum, $unwind, $unwindDelta, type Accumulators, type Arr, type AsLiteral, type Delta, type DeltaAccumulator, type DeltaAccumulators, Expr, type ExprHKT, type Exprs, type ExprsExact, type ExprsExactHKT, type ExprsPart, Field, type ID, type Loose, Machine, type Merge, type MergeArgs, type MergeInto, type MergeMapOArgs, type Model, type N, type NoRaw, type NullToOBJ, type O, type OPick, type OPickD, type RONoRaw, type RORec, type RawStages, type Rec, type Replace, type SnapshotStreamExecutionResult, type StrKey, type Strict, type TS, Type, type WriteonlyCollection, add, and, array, ceil, comp, concat, concatArray, createIndex, ctx, current, dateAdd, dateDiff, dateLt, datePart, dayAndMonthPart, divide, type doc, enablePreAndPostImages, eq, eqTyped, except, exprMapVal, field, fieldF, fieldM, filter, filterDefined, first, firstSure, floor, from, func, gt, gte, inArray, isArray, ite, last, log, lt, lte, makeCol, map1, mapVal, max, maxDate, mergeExact, mergeExact0, mergeExpr, mergeObjects, minDate, monthPart, multiply, ne, nil, noop, type notArr, notNull, now, or, pair, prepare, rand, range, root, set, setField, size, slice, sortArray, staging, startOf, str, sub, subtract, to, toInt, val, weekPart, wrap, year };
679
+ export { $accumulator, $and, $countDict, $entries, $eq, $exists, $expr, $getField, $group, $groupId, $groupMerge, $gt, $gtTs, $gte, $gteTs, $ifNull, $in, $insert, $insertX, $keys, $let, $lookup, $lt, $lte, $map, $map1, $match, $matchDelta, $merge, $merge_, $ne, $nin, $nor, $or, $outerLookup, $pushDict, $rand, $replaceWith, $set, $simpleInsert, $simpleMerge, $sum, $type, $unwind, $unwindDelta, type Accumulators, type Arr, type AsLiteral, type Delta, type DeltaAccumulator, type DeltaAccumulators, Expr, type ExprHKT, type Exprs, type ExprsExact, type ExprsExactHKT, type ExprsPart, Field, type ID, type Loose, Machine, type Merge, type MergeArgs, type MergeInto, type MergeMapOArgs, type Model, type MongoTypeNames, type N, type NoRaw, type NullToOBJ, type O, type OPick, type OPickD, type RONoRaw, type RORec, type RawStages, type Rec, type Replace, type SnapshotStreamExecutionResult, type StrKey, type Strict, type TS, Type, type WriteonlyCollection, add, and, array, ceil, comp, concat, concatArray, createIndex, ctx, current, dateAdd, dateDiff, dateLt, datePart, dayAndMonthPart, divide, type doc, enablePreAndPostImages, eq, eqTyped, except, exprMapVal, field, fieldF, fieldM, filter, filterDefined, first, firstSure, floor, from, func, gt, gte, inArray, isArray, ite, last, log, lt, lte, makeCol, map1, mapVal, max, maxDate, mergeExact, mergeExact0, mergeExpr, mergeObjects, minDate, monthPart, multiply, ne, nil, noop, type notArr, notNull, now, or, pair, prepare, rand, range, root, set, setField, size, slice, sortArray, staging, startOf, str, sub, subtract, to, toInt, val, weekPart, wrap, year };
package/index.esm.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { UUID, MongoClient } from 'mongodb';
2
2
  import { SynchronousPromise } from 'synchronous-promise';
3
3
  import crypto$1 from 'crypto';
4
- import { log as log$1 } from 'console';
5
4
  import { writeFile } from 'fs/promises';
6
5
 
7
6
  const asExprRaw = (raw) => ({ get: () => raw });
@@ -885,9 +884,6 @@ const $map = (ex, map) => $map1(ex, i => map(i.expr()));
885
884
  const operator = () => (op) => (operand) => {
886
885
  return {
887
886
  raw: { [op]: operand },
888
- expr: (field) => asExpr({
889
- raw: f => asExprRaw({ [op]: [$ifNull(field.expr(), nil).raw(f).get(), operand] }),
890
- }),
891
887
  };
892
888
  };
893
889
 
@@ -902,6 +898,8 @@ const $lt = comp('$lt');
902
898
  const dateLt = comp('$lt');
903
899
  const $gte = comp('$gte');
904
900
  const $lte = comp('$lte');
901
+ const $type = operator()('$type');
902
+ const $exists = operator()('$exists');
905
903
 
906
904
  const $expr = (expr) => ({
907
905
  raw: f => ({ $expr: expr.raw(f).get() }),
@@ -1384,16 +1382,16 @@ const replace = (s) => s.replace(/\{"\$timestamp":"(\d+)"\}/g, (_, d) => T(d));
1384
1382
  const json = (a) => replace(JSON.stringify(a));
1385
1383
  const log = (...args) => console.log(new Date(), ...args.map(a => (typeof a === 'function' ? a(replace) : a && typeof a === 'object' ? json(a) : a)));
1386
1384
 
1387
- const aggregate = (input, snapshot = true, start = Date.now()) => input(({ coll, input }) => {
1385
+ const aggregate = (streamName, input, snapshot = true, start = Date.now()) => input(({ coll, input }) => {
1388
1386
  const req = {
1389
1387
  aggregate: coll.collectionName,
1390
1388
  pipeline: input,
1391
1389
  cursor: {},
1392
1390
  ...(snapshot && { readConcern: { level: 'snapshot' } }),
1393
1391
  };
1394
- log('exec', req);
1392
+ log('exec', streamName, req);
1395
1393
  return coll.s.db.command(req).then(result => {
1396
- log('execed', req, result, 'took', Date.now() - start);
1394
+ log('execed', streamName, req, result, 'took', Date.now() - start);
1397
1395
  return result;
1398
1396
  }, err => {
1399
1397
  log('err', req, err);
@@ -1445,11 +1443,19 @@ const makeWatchStream = (db, { collection, projection: p, hardMatch: m }, startA
1445
1443
  },
1446
1444
  });
1447
1445
  pipeline.push({
1448
- $match: {
1449
- $or: [
1450
- { $expr: { $ne: ['$fullDocument', '$fullDocumentBeforeChange'] } },
1451
- Object.fromEntries(changeKeys.map(k => [k, null])),
1452
- ],
1446
+ $replaceWith: {
1447
+ ts: {
1448
+ $cond: {
1449
+ if: {
1450
+ $or: [
1451
+ { $ne: ['$fullDocument', '$fullDocumentBeforeChange'] },
1452
+ { $and: changeKeys.map(k => ({ $eq: [k, null] })) },
1453
+ ],
1454
+ },
1455
+ then: '$clusterTime',
1456
+ else: null,
1457
+ },
1458
+ },
1453
1459
  },
1454
1460
  });
1455
1461
  const stream = db.collection(collection.collectionName).watch(pipeline, {
@@ -1462,7 +1468,7 @@ const makeWatchStream = (db, { collection, projection: p, hardMatch: m }, startA
1462
1468
  if (doc)
1463
1469
  await new Promise(resolve => setTimeout(resolve, 100));
1464
1470
  if (doc)
1465
- log$1('detected', streamName, collection.collectionName, doc);
1471
+ log('detected', streamName, collection.collectionName, doc);
1466
1472
  return doc;
1467
1473
  };
1468
1474
  return { tryNext, close: () => stream.close() };
@@ -1484,8 +1490,9 @@ const executes$1 = (view, input, streamName) => {
1484
1490
  streamNames[streamName] = hash;
1485
1491
  else if (streamNames[streamName] != hash)
1486
1492
  throw new Error(`streamName ${streamName} already used`);
1487
- const { collection, projection, hardMatch, match } = view;
1488
- const removeNotYetSynchronizedFields = Object.values(mapExactToObject(projection, (_, k) => k.startsWith('_') ? root().of(k).has($ne(null)) : null));
1493
+ const { collection, projection, hardMatch: pre, match } = view;
1494
+ const removeNotYetSynchronizedFields = Object.values(mapExactToObject(projection, (_, k) => k.startsWith('_') ? root().of(k).has($exists(true)) : null));
1495
+ const hardMatch = $and(pre, ...removeNotYetSynchronizedFields);
1489
1496
  const job = {};
1490
1497
  const db = collection.s.db, coll = collection.collectionName;
1491
1498
  db.command({
@@ -1569,7 +1576,7 @@ const executes$1 = (view, input, streamName) => {
1569
1576
  const step3 = (lastTS) => async () => {
1570
1577
  const hardQuery = $and(lastTS
1571
1578
  ? root().of('touchedAt').has($gteTs(lastTS.ts))
1572
- : root().of('deletedAt').has($eq(null)), lastTS ? null : match && $expr(match), hardMatch, ...removeNotYetSynchronizedFields);
1579
+ : root().of('deletedAt').has($eq(null)), lastTS ? null : match && $expr(match), hardMatch);
1573
1580
  const notDeleted = eq($ifNull(root().of('deletedAt').expr(), nil))(nil);
1574
1581
  const query = match ? and(notDeleted, match) : notDeleted;
1575
1582
  const replaceRaw = $replaceWith_(field({
@@ -1587,7 +1594,7 @@ const executes$1 = (view, input, streamName) => {
1587
1594
  whenMatched: 'merge',
1588
1595
  whenNotMatched: 'insert',
1589
1596
  })).stages;
1590
- const r = await aggregate(c => c({ coll: collection, input: cloneIntoNew }));
1597
+ const r = await aggregate(streamName, c => c({ coll: collection, input: cloneIntoNew }));
1591
1598
  await snapshotCollection.deleteMany({ updated: true, after: null, before: null });
1592
1599
  return next(step4({ result: r, ts: lastTS?.ts }), 'run the aggregation');
1593
1600
  };
@@ -1595,7 +1602,7 @@ const executes$1 = (view, input, streamName) => {
1595
1602
  const step4 = ({ result, ts }) => async () => {
1596
1603
  const start = Date.now();
1597
1604
  await snapshotCollection.updateMany({ before: null }, { $set: { before: null } });
1598
- const aggResult = await aggregate(c => c({
1605
+ const aggResult = await aggregate(streamName, c => c({
1599
1606
  coll: snapshotCollection,
1600
1607
  input: link()
1601
1608
  .with($match_(root().of('updated').has($eq(true))))
@@ -1604,7 +1611,7 @@ const executes$1 = (view, input, streamName) => {
1604
1611
  .with(finalInput.raw(ts === undefined)).stages,
1605
1612
  }), false, start);
1606
1613
  const stream = makeStream(result.cursor.atClusterTime);
1607
- return next(step5({ result, aggResult, stream }), 'remove handled deleted updated', () => stream.close());
1614
+ return next(step5({ ts: result.cursor.atClusterTime, aggResult, stream }), 'remove handled deleted updated', () => stream.close());
1608
1615
  };
1609
1616
  const step5 = (l) => async () => {
1610
1617
  log(`remove handled deleted updated db['${snapshotCollection.collectionName}'].deleteMany({ updated: true, after: null })`);
@@ -1629,7 +1636,7 @@ const executes$1 = (view, input, streamName) => {
1629
1636
  const step7 = (l) => async () => {
1630
1637
  await last.updateOne({ _id: streamName }, {
1631
1638
  $set: {
1632
- ts: l.result.cursor.atClusterTime,
1639
+ ts: l.ts,
1633
1640
  data,
1634
1641
  },
1635
1642
  }, { upsert: true });
@@ -1638,11 +1645,15 @@ const executes$1 = (view, input, streamName) => {
1638
1645
  const step8 = (l) => {
1639
1646
  return nextData(l.aggResult.cursor.firstBatch)(() => l.stream
1640
1647
  .tryNext()
1641
- .catch(err => {
1648
+ .catch((err) => {
1642
1649
  log('restarting', err);
1643
- return 1;
1650
+ return { ts: null };
1644
1651
  })
1645
- .then(doc => (doc ? next(step2, 'restart') : step8(l))), 'wait for change');
1652
+ .then(doc => doc
1653
+ ? doc.ts
1654
+ ? next(step7({ ...l, ts: doc.ts }), 'nothing changed')
1655
+ : next(step2, 'restart')
1656
+ : step8(l)), 'wait for change');
1646
1657
  };
1647
1658
  return stop;
1648
1659
  };
@@ -1732,7 +1743,7 @@ const executes = (view, input, streamName) => {
1732
1743
  const makeStream = (startAt) => makeWatchStream(db, view, startAt, streamName);
1733
1744
  const step4 = (lastTS) => async () => {
1734
1745
  const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(lastTS.ts)), hardMatch, notDeleted, match && $expr(match));
1735
- const aggResult = await aggregate(c => c({
1746
+ const aggResult = await aggregate(streamName, c => c({
1736
1747
  coll: collection,
1737
1748
  input: link()
1738
1749
  .with($match_(hardQuery))
@@ -1741,17 +1752,23 @@ const executes = (view, input, streamName) => {
1741
1752
  .with(finalInput.raw(lastTS === null)).stages,
1742
1753
  }));
1743
1754
  const stream = makeStream(aggResult.cursor.atClusterTime);
1744
- return next(step7({ aggResult, result: aggResult, stream }), 'update __last', () => stream.close());
1755
+ return next(step7({ aggResult, ts: aggResult.cursor.atClusterTime, stream }), 'update __last', () => stream.close());
1745
1756
  };
1746
1757
  const step7 = (l) => async () => {
1747
- await last.updateOne({ _id: streamName }, { $set: { ts: l.result.cursor.atClusterTime, data } }, { upsert: true });
1758
+ await last.updateOne({ _id: streamName }, { $set: { ts: l.ts, data } }, { upsert: true });
1748
1759
  return step8(l);
1749
1760
  };
1750
1761
  const step8 = (l) => {
1751
1762
  return {
1752
1763
  data: l.aggResult.cursor.firstBatch,
1753
1764
  info: { job: undefined, debug: 'wait for change' },
1754
- cont: withStop(() => l.stream.tryNext().then(doc => (doc ? next(step1, 'restart') : step8(l)))),
1765
+ cont: withStop(() => l.stream
1766
+ .tryNext()
1767
+ .then(doc => doc
1768
+ ? doc.ts
1769
+ ? next(step7({ ...l, ts: doc.ts }), 'nothing changed')
1770
+ : next(step1, 'restart')
1771
+ : step8(l))),
1755
1772
  };
1756
1773
  };
1757
1774
  return stop;
@@ -1806,4 +1823,4 @@ const makeCol = async (docs, database, name) => {
1806
1823
  }
1807
1824
  };
1808
1825
 
1809
- export { $accumulator, $and, $countDict, $entries, $eq, $expr, $getField, $group, $groupId, $groupMerge, $gt, $gtTs, $gte, $gteTs, $ifNull, $in, $insert, $insertX, $keys, $let, $lookup, $lt, $lte, $map, $map1, $match, $matchDelta, $merge, $merge_, $ne, $nin, $nor, $or, $outerLookup, $pushDict, $rand, $replaceWith, $set, $simpleInsert, $simpleMerge, $sum, $unwind, $unwindDelta, Field, Machine, add, and, array, ceil, comp, concat$1 as concat, concatArray, createIndex, ctx, current, dateAdd, dateDiff, dateLt, datePart, dayAndMonthPart, divide, enablePreAndPostImages, eq, eqTyped, except, exprMapVal, field, fieldF, fieldM, filter, filterDefined, first$1 as first, firstSure, floor, from, func, gt, gte, inArray, isArray, ite, last, log, lt, lte, makeCol, map1, mapVal, max, maxDate, mergeExact, mergeExact0, mergeExpr, mergeObjects, minDate, monthPart, multiply, ne, nil, noop, notNull, now, or, pair, prepare, rand, range, root, set, setField, size, slice, sortArray, staging, startOf, str, sub, subtract, to, toInt, val, weekPart, wrap, year };
1826
+ export { $accumulator, $and, $countDict, $entries, $eq, $exists, $expr, $getField, $group, $groupId, $groupMerge, $gt, $gtTs, $gte, $gteTs, $ifNull, $in, $insert, $insertX, $keys, $let, $lookup, $lt, $lte, $map, $map1, $match, $matchDelta, $merge, $merge_, $ne, $nin, $nor, $or, $outerLookup, $pushDict, $rand, $replaceWith, $set, $simpleInsert, $simpleMerge, $sum, $type, $unwind, $unwindDelta, Field, Machine, add, and, array, ceil, comp, concat$1 as concat, concatArray, createIndex, ctx, current, dateAdd, dateDiff, dateLt, datePart, dayAndMonthPart, divide, enablePreAndPostImages, eq, eqTyped, except, exprMapVal, field, fieldF, fieldM, filter, filterDefined, first$1 as first, firstSure, floor, from, func, gt, gte, inArray, isArray, ite, last, log, lt, lte, makeCol, map1, mapVal, max, maxDate, mergeExact, mergeExact0, mergeExpr, mergeObjects, minDate, monthPart, multiply, ne, nil, noop, notNull, now, or, pair, prepare, rand, range, root, set, setField, size, slice, sortArray, staging, startOf, str, sub, subtract, to, toInt, val, weekPart, wrap, year };
package/index.js CHANGED
@@ -3,7 +3,6 @@
3
3
  var mongodb = require('mongodb');
4
4
  var synchronousPromise = require('synchronous-promise');
5
5
  var crypto$1 = require('crypto');
6
- var console$1 = require('console');
7
6
  var promises = require('fs/promises');
8
7
 
9
8
  const asExprRaw = (raw) => ({ get: () => raw });
@@ -887,9 +886,6 @@ const $map = (ex, map) => $map1(ex, i => map(i.expr()));
887
886
  const operator = () => (op) => (operand) => {
888
887
  return {
889
888
  raw: { [op]: operand },
890
- expr: (field) => asExpr({
891
- raw: f => asExprRaw({ [op]: [$ifNull(field.expr(), nil).raw(f).get(), operand] }),
892
- }),
893
889
  };
894
890
  };
895
891
 
@@ -904,6 +900,8 @@ const $lt = comp('$lt');
904
900
  const dateLt = comp('$lt');
905
901
  const $gte = comp('$gte');
906
902
  const $lte = comp('$lte');
903
+ const $type = operator()('$type');
904
+ const $exists = operator()('$exists');
907
905
 
908
906
  const $expr = (expr) => ({
909
907
  raw: f => ({ $expr: expr.raw(f).get() }),
@@ -1386,16 +1384,16 @@ const replace = (s) => s.replace(/\{"\$timestamp":"(\d+)"\}/g, (_, d) => T(d));
1386
1384
  const json = (a) => replace(JSON.stringify(a));
1387
1385
  const log = (...args) => console.log(new Date(), ...args.map(a => (typeof a === 'function' ? a(replace) : a && typeof a === 'object' ? json(a) : a)));
1388
1386
 
1389
- const aggregate = (input, snapshot = true, start = Date.now()) => input(({ coll, input }) => {
1387
+ const aggregate = (streamName, input, snapshot = true, start = Date.now()) => input(({ coll, input }) => {
1390
1388
  const req = {
1391
1389
  aggregate: coll.collectionName,
1392
1390
  pipeline: input,
1393
1391
  cursor: {},
1394
1392
  ...(snapshot && { readConcern: { level: 'snapshot' } }),
1395
1393
  };
1396
- log('exec', req);
1394
+ log('exec', streamName, req);
1397
1395
  return coll.s.db.command(req).then(result => {
1398
- log('execed', req, result, 'took', Date.now() - start);
1396
+ log('execed', streamName, req, result, 'took', Date.now() - start);
1399
1397
  return result;
1400
1398
  }, err => {
1401
1399
  log('err', req, err);
@@ -1447,11 +1445,19 @@ const makeWatchStream = (db, { collection, projection: p, hardMatch: m }, startA
1447
1445
  },
1448
1446
  });
1449
1447
  pipeline.push({
1450
- $match: {
1451
- $or: [
1452
- { $expr: { $ne: ['$fullDocument', '$fullDocumentBeforeChange'] } },
1453
- Object.fromEntries(changeKeys.map(k => [k, null])),
1454
- ],
1448
+ $replaceWith: {
1449
+ ts: {
1450
+ $cond: {
1451
+ if: {
1452
+ $or: [
1453
+ { $ne: ['$fullDocument', '$fullDocumentBeforeChange'] },
1454
+ { $and: changeKeys.map(k => ({ $eq: [k, null] })) },
1455
+ ],
1456
+ },
1457
+ then: '$clusterTime',
1458
+ else: null,
1459
+ },
1460
+ },
1455
1461
  },
1456
1462
  });
1457
1463
  const stream = db.collection(collection.collectionName).watch(pipeline, {
@@ -1464,7 +1470,7 @@ const makeWatchStream = (db, { collection, projection: p, hardMatch: m }, startA
1464
1470
  if (doc)
1465
1471
  await new Promise(resolve => setTimeout(resolve, 100));
1466
1472
  if (doc)
1467
- console$1.log('detected', streamName, collection.collectionName, doc);
1473
+ log('detected', streamName, collection.collectionName, doc);
1468
1474
  return doc;
1469
1475
  };
1470
1476
  return { tryNext, close: () => stream.close() };
@@ -1486,8 +1492,9 @@ const executes$1 = (view, input, streamName) => {
1486
1492
  streamNames[streamName] = hash;
1487
1493
  else if (streamNames[streamName] != hash)
1488
1494
  throw new Error(`streamName ${streamName} already used`);
1489
- const { collection, projection, hardMatch, match } = view;
1490
- const removeNotYetSynchronizedFields = Object.values(mapExactToObject(projection, (_, k) => k.startsWith('_') ? root().of(k).has($ne(null)) : null));
1495
+ const { collection, projection, hardMatch: pre, match } = view;
1496
+ const removeNotYetSynchronizedFields = Object.values(mapExactToObject(projection, (_, k) => k.startsWith('_') ? root().of(k).has($exists(true)) : null));
1497
+ const hardMatch = $and(pre, ...removeNotYetSynchronizedFields);
1491
1498
  const job = {};
1492
1499
  const db = collection.s.db, coll = collection.collectionName;
1493
1500
  db.command({
@@ -1571,7 +1578,7 @@ const executes$1 = (view, input, streamName) => {
1571
1578
  const step3 = (lastTS) => async () => {
1572
1579
  const hardQuery = $and(lastTS
1573
1580
  ? root().of('touchedAt').has($gteTs(lastTS.ts))
1574
- : root().of('deletedAt').has($eq(null)), lastTS ? null : match && $expr(match), hardMatch, ...removeNotYetSynchronizedFields);
1581
+ : root().of('deletedAt').has($eq(null)), lastTS ? null : match && $expr(match), hardMatch);
1575
1582
  const notDeleted = eq($ifNull(root().of('deletedAt').expr(), nil))(nil);
1576
1583
  const query = match ? and(notDeleted, match) : notDeleted;
1577
1584
  const replaceRaw = $replaceWith_(field({
@@ -1589,7 +1596,7 @@ const executes$1 = (view, input, streamName) => {
1589
1596
  whenMatched: 'merge',
1590
1597
  whenNotMatched: 'insert',
1591
1598
  })).stages;
1592
- const r = await aggregate(c => c({ coll: collection, input: cloneIntoNew }));
1599
+ const r = await aggregate(streamName, c => c({ coll: collection, input: cloneIntoNew }));
1593
1600
  await snapshotCollection.deleteMany({ updated: true, after: null, before: null });
1594
1601
  return next(step4({ result: r, ts: lastTS?.ts }), 'run the aggregation');
1595
1602
  };
@@ -1597,7 +1604,7 @@ const executes$1 = (view, input, streamName) => {
1597
1604
  const step4 = ({ result, ts }) => async () => {
1598
1605
  const start = Date.now();
1599
1606
  await snapshotCollection.updateMany({ before: null }, { $set: { before: null } });
1600
- const aggResult = await aggregate(c => c({
1607
+ const aggResult = await aggregate(streamName, c => c({
1601
1608
  coll: snapshotCollection,
1602
1609
  input: link()
1603
1610
  .with($match_(root().of('updated').has($eq(true))))
@@ -1606,7 +1613,7 @@ const executes$1 = (view, input, streamName) => {
1606
1613
  .with(finalInput.raw(ts === undefined)).stages,
1607
1614
  }), false, start);
1608
1615
  const stream = makeStream(result.cursor.atClusterTime);
1609
- return next(step5({ result, aggResult, stream }), 'remove handled deleted updated', () => stream.close());
1616
+ return next(step5({ ts: result.cursor.atClusterTime, aggResult, stream }), 'remove handled deleted updated', () => stream.close());
1610
1617
  };
1611
1618
  const step5 = (l) => async () => {
1612
1619
  log(`remove handled deleted updated db['${snapshotCollection.collectionName}'].deleteMany({ updated: true, after: null })`);
@@ -1631,7 +1638,7 @@ const executes$1 = (view, input, streamName) => {
1631
1638
  const step7 = (l) => async () => {
1632
1639
  await last.updateOne({ _id: streamName }, {
1633
1640
  $set: {
1634
- ts: l.result.cursor.atClusterTime,
1641
+ ts: l.ts,
1635
1642
  data,
1636
1643
  },
1637
1644
  }, { upsert: true });
@@ -1640,11 +1647,15 @@ const executes$1 = (view, input, streamName) => {
1640
1647
  const step8 = (l) => {
1641
1648
  return nextData(l.aggResult.cursor.firstBatch)(() => l.stream
1642
1649
  .tryNext()
1643
- .catch(err => {
1650
+ .catch((err) => {
1644
1651
  log('restarting', err);
1645
- return 1;
1652
+ return { ts: null };
1646
1653
  })
1647
- .then(doc => (doc ? next(step2, 'restart') : step8(l))), 'wait for change');
1654
+ .then(doc => doc
1655
+ ? doc.ts
1656
+ ? next(step7({ ...l, ts: doc.ts }), 'nothing changed')
1657
+ : next(step2, 'restart')
1658
+ : step8(l)), 'wait for change');
1648
1659
  };
1649
1660
  return stop;
1650
1661
  };
@@ -1734,7 +1745,7 @@ const executes = (view, input, streamName) => {
1734
1745
  const makeStream = (startAt) => makeWatchStream(db, view, startAt, streamName);
1735
1746
  const step4 = (lastTS) => async () => {
1736
1747
  const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(lastTS.ts)), hardMatch, notDeleted, match && $expr(match));
1737
- const aggResult = await aggregate(c => c({
1748
+ const aggResult = await aggregate(streamName, c => c({
1738
1749
  coll: collection,
1739
1750
  input: link()
1740
1751
  .with($match_(hardQuery))
@@ -1743,17 +1754,23 @@ const executes = (view, input, streamName) => {
1743
1754
  .with(finalInput.raw(lastTS === null)).stages,
1744
1755
  }));
1745
1756
  const stream = makeStream(aggResult.cursor.atClusterTime);
1746
- return next(step7({ aggResult, result: aggResult, stream }), 'update __last', () => stream.close());
1757
+ return next(step7({ aggResult, ts: aggResult.cursor.atClusterTime, stream }), 'update __last', () => stream.close());
1747
1758
  };
1748
1759
  const step7 = (l) => async () => {
1749
- await last.updateOne({ _id: streamName }, { $set: { ts: l.result.cursor.atClusterTime, data } }, { upsert: true });
1760
+ await last.updateOne({ _id: streamName }, { $set: { ts: l.ts, data } }, { upsert: true });
1750
1761
  return step8(l);
1751
1762
  };
1752
1763
  const step8 = (l) => {
1753
1764
  return {
1754
1765
  data: l.aggResult.cursor.firstBatch,
1755
1766
  info: { job: undefined, debug: 'wait for change' },
1756
- cont: withStop(() => l.stream.tryNext().then(doc => (doc ? next(step1, 'restart') : step8(l)))),
1767
+ cont: withStop(() => l.stream
1768
+ .tryNext()
1769
+ .then(doc => doc
1770
+ ? doc.ts
1771
+ ? next(step7({ ...l, ts: doc.ts }), 'nothing changed')
1772
+ : next(step1, 'restart')
1773
+ : step8(l))),
1757
1774
  };
1758
1775
  };
1759
1776
  return stop;
@@ -1813,6 +1830,7 @@ exports.$and = $and;
1813
1830
  exports.$countDict = $countDict;
1814
1831
  exports.$entries = $entries;
1815
1832
  exports.$eq = $eq;
1833
+ exports.$exists = $exists;
1816
1834
  exports.$expr = $expr;
1817
1835
  exports.$getField = $getField;
1818
1836
  exports.$group = $group;
@@ -1849,6 +1867,7 @@ exports.$set = $set;
1849
1867
  exports.$simpleInsert = $simpleInsert;
1850
1868
  exports.$simpleMerge = $simpleMerge;
1851
1869
  exports.$sum = $sum;
1870
+ exports.$type = $type;
1852
1871
  exports.$unwind = $unwind;
1853
1872
  exports.$unwindDelta = $unwindDelta;
1854
1873
  exports.Field = Field;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "module": "index.esm.js",
4
4
  "typings": "index.d.ts",
5
5
  "name": "@omegup/msync",
6
- "version": "0.0.47",
6
+ "version": "0.0.49",
7
7
  "dependencies": {
8
8
  "dayjs": "^1.11.9",
9
9
  "dotenv": "^16.3.1",