@omegup/msync 0.0.1

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 +605 -0
  2. package/index.esm.js +1583 -0
  3. package/index.js +1684 -0
  4. package/package.json +12 -0
package/index.js ADDED
@@ -0,0 +1,1684 @@
1
+ 'use strict';
2
+
3
+ var mongodb = require('mongodb');
4
+ var crypto = require('crypto');
5
+ var promises = require('fs/promises');
6
+
7
+ const asExprRaw = (raw) => ({ get: () => raw });
8
+ const asExpr = (r) => r;
9
+ const asBoolExpr = (r) => r;
10
+
11
+ const max = (...expr) => asExpr({
12
+ raw: f => asExprRaw({ $max: expr.map(e => e.raw(f).get()) }),
13
+ });
14
+ const lt = (...expr) => asExpr({
15
+ raw: f => asExprRaw({ $lt: expr.map(e => e.raw(f).get()) }),
16
+ });
17
+ const gt = (...expr) => asExpr({
18
+ raw: f => asExprRaw({ $gt: expr.map(e => e.raw(f).get()) }),
19
+ });
20
+ const lte = (...expr) => asExpr({
21
+ raw: f => asExprRaw({ $lte: expr.map(e => e.raw(f).get()) }),
22
+ });
23
+ const gte = (...expr) => asExpr({
24
+ raw: f => asExprRaw({ $gte: expr.map(e => e.raw(f).get()) }),
25
+ });
26
+ function subtract(...expr) {
27
+ return asExpr({
28
+ raw: f => asExprRaw({ $subtract: expr.map(e => e.raw(f).get()) }),
29
+ });
30
+ }
31
+ function add(...expr) {
32
+ return asExpr({
33
+ raw: f => asExprRaw({ $add: expr.map(e => e.raw(f).get()) }),
34
+ });
35
+ }
36
+ function divide(...expr) {
37
+ return asExpr({
38
+ raw: f => asExprRaw({ $divide: expr.map(e => e.raw(f).get()) }),
39
+ });
40
+ }
41
+ function multiply(...expr) {
42
+ return asExpr({
43
+ raw: f => asExprRaw({ $multiply: expr.map(e => e.raw(f).get()) }),
44
+ });
45
+ }
46
+ function floor(expr) {
47
+ return asExpr({
48
+ raw: f => asExprRaw({ $floor: expr.raw(f).get() }),
49
+ });
50
+ }
51
+ function ceil(expr) {
52
+ return asExpr({
53
+ raw: f => asExprRaw({ $ceil: expr.raw(f).get() }),
54
+ });
55
+ }
56
+
57
+ const id$1 = (x) => x;
58
+ const defined = (x) => x != null;
59
+ const map1 = (k, to) => {
60
+ const k2 = k;
61
+ return { [k]: [k2, to] };
62
+ };
63
+
64
+ const map = (x, f) => Object.fromEntries(Object.entries(x).map(([k, v]) => [k, f(v, k)]));
65
+ const mapExactToObject0 = (x, f) => {
66
+ const filter = (x) => x.filter((x) => x[0] === x[1][0]);
67
+ const matched = filter(Object.entries(x));
68
+ return Object.fromEntries(matched.map(([k, v]) => [k, f(v[1], k)]));
69
+ };
70
+ const mapExactToObject1 = (x, f) => mapExactToObject0(x, f);
71
+ const mapExact0 = (x, f) => mapExactToObject0(x, (v, k) => [k, f(v, k)]);
72
+ const mapExact1 = (x, f) => mapExactToObject1(x, (v, k) => [k, f(v, k)]);
73
+ const mapExactToObject = (x, f) => mapExactToObject0(x, f);
74
+ const mapExact = (x, f) => mapExactToObject(x, (v, k) => [k, f(v, k)]);
75
+ const spread = (a, b) => ({ ...a, ...mapExact(b, id$1) });
76
+
77
+ const val = (val) => asExpr({
78
+ raw: () => asExprRaw((val && typeof val === 'object') || (typeof val === 'string' && val[0] === '$')
79
+ ? { $literal: val }
80
+ : val),
81
+ });
82
+ const current = asExpr({
83
+ raw: () => asExprRaw('$$CLUSTER_TIME'),
84
+ });
85
+ const $let = (vars, inExpr) => asExpr({
86
+ raw: f => asExprRaw({
87
+ $let: {
88
+ vars: mapExactToObject(vars, v => v.raw(f).get()),
89
+ in: inExpr.raw(f).get(),
90
+ },
91
+ }),
92
+ });
93
+ const nil = val(null);
94
+ const $getField = (expr, field) => asExpr({
95
+ raw: f => asExprRaw({
96
+ $getField: {
97
+ field: field,
98
+ input: expr.raw(f).get(),
99
+ },
100
+ }),
101
+ });
102
+ const func = (f, ...args) => asExpr({
103
+ raw: field => asExprRaw({
104
+ $function: {
105
+ body: f.toString(),
106
+ args: args.map(x => x.raw(field).get()),
107
+ lang: 'js',
108
+ },
109
+ }),
110
+ });
111
+ const rand = function () {
112
+ const s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
113
+ return Array(17)
114
+ .fill(0)
115
+ .map(() => s[Math.floor(Math.random() * 62)])
116
+ .join('');
117
+ };
118
+ const $rand = func(rand);
119
+
120
+ const ite = ((cond, then, orelse) => asExpr({
121
+ raw: (f) => asExprRaw({
122
+ $cond: {
123
+ if: cond.raw(f).get(),
124
+ then: then.raw(f).get(),
125
+ else: orelse.raw(f).get(),
126
+ },
127
+ }),
128
+ }));
129
+ const and = (...expr) => asExpr({
130
+ raw: f => asExprRaw({ $and: expr.map(e => e.raw(f).get()) }),
131
+ });
132
+ const eq = (a) => (b) => asExpr({
133
+ raw: f => asExprRaw({ $eq: [a.raw(f).get(), b.raw(f).get()] }),
134
+ });
135
+ const sub = (a, f) => asExpr({
136
+ raw: (g) => asExprRaw(a.raw(g.with(f)).get()),
137
+ });
138
+ const eqTyped = (a, b) => asBoolExpr({
139
+ raw: (f) => asExprRaw({ $eq: [a.raw(f).get(), b.raw(f).get()] }),
140
+ });
141
+ const ne = (a) => (b) => asExpr({
142
+ raw: f => asExprRaw({ $ne: [a.raw(f).get(), b.raw(f).get()] }),
143
+ });
144
+ const notNull = (a) => ne($ifNull(a, nil))(nil);
145
+ const $ifNull = (...expr) => asExpr({
146
+ raw: f => asExprRaw({ $ifNull: expr.map(e => e.raw(f).get()) }),
147
+ });
148
+ const exprMapVal = (expr, map, or) => asExpr({
149
+ raw: f => asExprRaw({
150
+ $switch: {
151
+ branches: Object.entries(map).map(([k, v]) => ({
152
+ case: { $eq: [expr.raw(f).get(), { $literal: k }] },
153
+ then: v.raw(f).get(),
154
+ })),
155
+ ...(or && { default: or.raw(f).get() }),
156
+ },
157
+ }),
158
+ });
159
+ const mapVal = (expr, map, or) => exprMapVal(expr, Object.fromEntries(Object.entries(map).map(([k, v]) => [k, val(v)])), val(or));
160
+ const setField = ({ field, input, value, }) => asExpr({
161
+ raw: f => asExprRaw({
162
+ $setField: {
163
+ field: field.raw(f).get(),
164
+ input: input.raw(f).get(),
165
+ value: value.raw(f).get(),
166
+ },
167
+ }),
168
+ });
169
+
170
+ class Field {
171
+ field;
172
+ raw;
173
+ concat;
174
+ has(p) {
175
+ return { raw: f => ({ [f.with(this).str()]: p.raw }) };
176
+ }
177
+ constructor(field, raw, concat) {
178
+ this.field = field;
179
+ this.raw = raw;
180
+ this.concat = concat;
181
+ }
182
+ static root = () => {
183
+ const concat = (f1, f2) => new Field([f1.field, f2.field].filter(id$1).join('.'), f1.raw, f1.concat);
184
+ return new Field('', s => (s ? `$${s}` : '$$ROOT'), concat);
185
+ };
186
+ static ctx = () => (k) => {
187
+ const concat = (_, f2) => new Field(f2.field, f2.raw, f2.concat);
188
+ return new Field(k, s => `$$${s}`, concat);
189
+ };
190
+ of(k) {
191
+ return new Field([this.field, k].filter(id$1).join('.'), this.raw, this.concat);
192
+ }
193
+ with(k) {
194
+ return k.concat(this, k);
195
+ }
196
+ str() {
197
+ return this.field;
198
+ }
199
+ exprRaw() {
200
+ return asExprRaw(this.raw(this.field));
201
+ }
202
+ expr() {
203
+ return asExpr({
204
+ raw: (f) => f.with(this).exprRaw(),
205
+ });
206
+ }
207
+ }
208
+ const { root, ctx } = Field;
209
+
210
+ const asAccumulator = (x) => x;
211
+ const $sum_ = (expr) => ({ raw: f => asAccumulator({ $sum: expr.raw(f).get() }) });
212
+ const $sum = (expr) => ({
213
+ group: $sum_(ite(root().of('old').expr(), subtract(val(0), $ifNull(sub(expr, root().of('v')), val(0))), sub(expr, root().of('v')))),
214
+ merge: (x, y) => add($ifNull(x, val(0)), y),
215
+ });
216
+ const $accumulator_ = (init, accumulateArgs, accumulate, merge) => {
217
+ return {
218
+ raw: f => asAccumulator({
219
+ $accumulator: {
220
+ init: init.toString(),
221
+ initArgs: [],
222
+ accumulate: accumulate.toString(),
223
+ accumulateArgs: accumulateArgs.map(e => e.raw(f).get()),
224
+ merge: merge.toString(),
225
+ lang: 'js',
226
+ },
227
+ }),
228
+ };
229
+ };
230
+ const $accumulator = (init, accumulateArgs, accumulate, merge) => ({
231
+ group: $accumulator_(init, accumulateArgs, accumulate, merge),
232
+ merge: (a, b) => func(merge, a, b),
233
+ });
234
+ const $countDict = (expr) => $accumulator(function () {
235
+ return {};
236
+ }, [sub(expr, root().of('v')), root().of('old').expr()], function (a, k, old) {
237
+ let y = (a[k] || 0) + (old ? -1 : 1);
238
+ return y ? (a[k] = y) : delete a[k], a;
239
+ }, function (a, b) {
240
+ return Object.keys(b).reduce((a, k) => {
241
+ let y = (a[k] || 0) + b[k];
242
+ return y ? (a[k] = y) : delete a[k], a;
243
+ }, a || {});
244
+ });
245
+ const $pushDict = (key, value) => $accumulator(function () {
246
+ return {};
247
+ }, [
248
+ sub(key, root().of('v')),
249
+ sub(value, root().of('v')),
250
+ root().of('old').expr(),
251
+ ], function (ra, k, v, old) {
252
+ let a = { ...ra };
253
+ const equal = (a, b) => {
254
+ if ([a, b].some(a => !a || typeof a != 'object'))
255
+ return a === b;
256
+ const keys = Object.keys(a);
257
+ return keys.length === Object.keys(b).length && keys.every(k => equal(a[k], b[k]));
258
+ };
259
+ const delta = (a[k] ||= { 0: [], 1: [] }), addR = delta[old ? 1 : 0], rmR = delta[old ? 0 : 1], idx = rmR.findIndex(x => equal(x, v));
260
+ if (idx !== -1)
261
+ rmR.splice(idx, 1);
262
+ else
263
+ addR.push(v);
264
+ return a;
265
+ }, function (acc, b) {
266
+ return Object.keys(b).reduce((a, k) => {
267
+ return Object.entries({ ...b[k] }).reduce((a, [p, v]) => v.reduce((a, v) => {
268
+ const equal = (a, b) => {
269
+ if ([a, b].some(a => !a || typeof a != 'object'))
270
+ return a === b;
271
+ const keys = Object.keys(a);
272
+ return keys.length === Object.keys(b).length && keys.every(k => equal(a[k], b[k]));
273
+ };
274
+ const delta = (a[k] ||= { 0: [], 1: [] }), addR = delta[p], rmR = delta[+p ? 0 : 1], idx = rmR.findIndex(x => equal(x, v));
275
+ if (idx !== -1)
276
+ rmR.splice(idx, 1);
277
+ else
278
+ addR.push(v);
279
+ return a;
280
+ }, a), a);
281
+ }, acc || {});
282
+ });
283
+ const $keys = (expr) => func(function (obj) {
284
+ return Object.keys(obj);
285
+ }, expr);
286
+ const $entries = (expr) => func(function (obj) {
287
+ return Object.entries({ ...obj }).flatMap(([k, v]) => v[0].map(v => ({ k, v })));
288
+ }, expr);
289
+
290
+ const concat$2 = (...expr) => asExpr({
291
+ raw: f => asExprRaw({ $concat: expr.map(e => e.raw(f).get()) }),
292
+ });
293
+ const fieldM = (expr, m) => asExpr({
294
+ raw: (f) => asExprRaw(Object.fromEntries(Object.entries(m).map(([dom, ref]) => [
295
+ dom,
296
+ expr[ref].raw(f).get(),
297
+ ]))),
298
+ });
299
+ const mergeExact = (...[exprsExact1, exprsExact2]) => spread(exprsExact1, exprsExact2);
300
+ const mergeExpr = (...exprs) => mergeExact(...exprs);
301
+ const fieldF = () => (exprs) => Object.keys(exprs).length
302
+ ? asExpr({
303
+ raw: f => asExprRaw(mapExactToObject(exprs, e => e.raw(f).get())),
304
+ })
305
+ : val({});
306
+ const field = fieldF();
307
+
308
+ const id = (x) => x;
309
+ const assertEqual = () => ({
310
+ forward: id,
311
+ backward: id,
312
+ forward1: id,
313
+ backward1: id,
314
+ });
315
+ const omitRORec = () => assertEqual();
316
+ const omitPick = () => assertEqual();
317
+
318
+ const asStages = (x) => x;
319
+ const concatStages = (part1, part2) => asStages([...part1, ...part2]);
320
+ const concatDelta = (part1, part2) => ({
321
+ delta: concatStages(part1.delta, part2.delta),
322
+ raw: f => concatStages(part1.raw(f), part2.raw(f)),
323
+ });
324
+ const pipe = (stream, s, concat, empty) => {
325
+ const acc = {
326
+ with: (map) => pipe(map(i => stream(concat(s, i))), empty(), concat, empty),
327
+ then: (next) => pipe(stream, concat(s, next), concat, empty),
328
+ get: () => stream(s),
329
+ };
330
+ return acc;
331
+ };
332
+ const concat$1 = (stages) => ({
333
+ with: extra => concat$1(concatStages(stages, extra)),
334
+ stages,
335
+ });
336
+ const link = () => ({
337
+ with: extra => concat$1(extra),
338
+ stages: asStages([]),
339
+ });
340
+ const emptyDelta = () => ({
341
+ delta: link().stages,
342
+ raw: () => link().stages,
343
+ });
344
+ const concatTStages = ({ coll, exec, input }, stages) => ({ coll, input, exec: concatStages(exec, stages) });
345
+
346
+ const size = (expr) => asExpr({
347
+ raw: f => asExprRaw({ $size: expr.raw(f).get() }),
348
+ });
349
+ const filterDefined = (expr) => filter({
350
+ expr,
351
+ as: 'x',
352
+ cond: ctx()('x').expr(),
353
+ });
354
+ const filter = ({ as, cond, expr, limit, }) => asExpr({
355
+ raw: f => asExprRaw({
356
+ $filter: {
357
+ input: expr.raw(f).get(),
358
+ as,
359
+ cond: cond.raw(f).get(),
360
+ limit: limit?.raw(f).get(),
361
+ },
362
+ }),
363
+ });
364
+ const sortArray = ({ sortBy, expr, order, }) => asExpr({
365
+ raw: f => asExprRaw({ $sortArray: { input: expr.raw(f).get(), sortBy: { [sortBy]: order ?? -1 } } }),
366
+ });
367
+ const isArray = (expr) => asBoolExpr({
368
+ raw: f => asExprRaw({ $isArray: expr.raw(f).get() }),
369
+ });
370
+ const array = (...exprs) => asExpr({
371
+ raw: f => asExprRaw(exprs.map(x => x.raw(f).get())),
372
+ });
373
+ const concat = (...exprs) => asExpr({
374
+ raw: f => asExprRaw({ $concatArrays: exprs.map(x => x.raw(f).get()) }),
375
+ });
376
+ const first$1 = (expr) => asExpr({
377
+ raw: f => asExprRaw({ $first: expr.raw(f).get() }),
378
+ });
379
+ const firstSure = first$1;
380
+ const last = (expr) => asExpr({
381
+ raw: f => asExprRaw({ $last: expr.raw(f).get() }),
382
+ });
383
+ const mergeObjects = (...exprs) => asExpr({
384
+ raw: f => asExprRaw({ $mergeObjects: exprs.map(x => x.raw(f).get()) }),
385
+ });
386
+ const inArray = (...exprs) => asExpr({
387
+ raw: f => asExprRaw({ $in: exprs.map(x => x.raw(f).get()) }),
388
+ });
389
+ const reduce = (input, initialValue, inExpr) => asExpr({
390
+ raw: f => asExprRaw({
391
+ $reduce: {
392
+ input: input.raw(f).get(),
393
+ initialValue: initialValue.raw(f).get(),
394
+ in: inExpr.raw(f).get(),
395
+ },
396
+ }),
397
+ });
398
+ const indexOfArray = (array, item) => asExpr({
399
+ raw: f => asExprRaw({
400
+ $indexOfArray: [array.raw(f).get(), item.raw(f).get()],
401
+ }),
402
+ });
403
+ const slice = (array, start, end) => asExpr({
404
+ raw: f => asExprRaw({
405
+ $slice: [array.raw(f).get(), start.raw(f).get(), end.raw(f).get()],
406
+ }),
407
+ });
408
+ const except = (a, b) => {
409
+ const value = ctx()('value');
410
+ const out = value.of('out').expr();
411
+ const except = value.of('except').expr();
412
+ const curr = ctx()('this').expr();
413
+ const indexInExcept = ctx()('indexInExcept').expr();
414
+ return $let({
415
+ res: [
416
+ 'res',
417
+ reduce(a, field({ out: ['out', array()], except: ['except', b] }), $let({
418
+ indexInExcept: ['indexInExcept', indexOfArray(except, curr)],
419
+ }, ite(gte(indexInExcept, val(0)), field({
420
+ out: ['out', out],
421
+ except: [
422
+ 'except',
423
+ concat(ite(eq(indexInExcept)(val(0)), array(), slice(except, val(0), indexInExcept)), slice(except, add(indexInExcept, val(1)), size(except))),
424
+ ],
425
+ }), field({
426
+ out: ['out', concat(out, array(curr))],
427
+ except: ['except', except],
428
+ })))),
429
+ ],
430
+ }, ctx()('res').of('out').expr());
431
+ };
432
+
433
+ const $match1 = (query) => f => asStages(query ? [{ $match: query.raw(f()) }] : []);
434
+ const $set1 = (updater) => f => asStages([
435
+ {
436
+ $set: Object.fromEntries(updater.raw(f()).map(([k, v]) => [
437
+ f()
438
+ .of(k.slice(1))
439
+ .str(),
440
+ v,
441
+ ])),
442
+ },
443
+ ]);
444
+ const $project1 = (projection) => asStages([
445
+ {
446
+ $project: mapExactToObject(projection, () => 1),
447
+ },
448
+ ]);
449
+ const $replaceWith1 = (expr) => f => {
450
+ const parts = f().str().split('.').filter(id$1);
451
+ return asStages([
452
+ { $replaceWith: parts.reduce((v, k) => ({ [k]: v }), expr.raw(f()).get()) },
453
+ ]);
454
+ };
455
+ const $unwind1 = (k) => f => asStages([{ $unwind: `$${f().of(k).str()}` }]);
456
+ const $group1 = (id, args) => (f) => asStages([
457
+ {
458
+ $group: {
459
+ _id: id.raw(f()).get(),
460
+ ...mapExactToObject(args, v => v.raw(f())),
461
+ },
462
+ },
463
+ ]);
464
+ const rawVars = (vars, f) => mapExactToObject(vars, v => v.raw(f).get());
465
+ const $simpleLookup1 = (args) => f => {
466
+ const { coll, k, vars, fields, ...etc } = args;
467
+ return asStages([
468
+ {
469
+ $lookup: {
470
+ ...(coll && { from: coll.collectionName }),
471
+ ...(fields && {
472
+ localField: f().with(fields.local).str(),
473
+ foreignField: root().with(fields.foreign).str(),
474
+ }),
475
+ as: f().of(k).str(),
476
+ let: rawVars(vars, f()),
477
+ ...etc,
478
+ },
479
+ },
480
+ ]);
481
+ };
482
+
483
+ const $match_ = (query) => $match1(query)(root);
484
+ const $set_ = (updater) => $set1(updater)(root);
485
+ const $replaceWith_ = (expr) => $replaceWith1(expr)(root);
486
+ const $unwind_ = (k) => $unwind1(k)(root);
487
+ const $group_ = () => (id, args) => $group1(id, args)(root);
488
+ const $project_ = $project1;
489
+ const $simpleLookup_ = (args) => $simpleLookup1(args)(root);
490
+
491
+ const subGroup = (id, args, addGrp) => {
492
+ const part = filterDefined(array(ite(eqTyped(root().of('after').expr(), nil), nil, field({
493
+ v: ['v', root().of('after').expr()],
494
+ old: ['old', val(false)],
495
+ })), ite(eqTyped(root().of('before').expr(), nil), nil, field({
496
+ v: ['v', root().of('before').expr()],
497
+ old: ['old', val(true)],
498
+ }))));
499
+ const replaceWith = (expr) => $replaceWith_(expr);
500
+ return link()
501
+ .with($replaceWith_(field({
502
+ part: ['part', part],
503
+ })))
504
+ .with($unwind_('part'))
505
+ .with($replaceWith_(root().of('part').expr()))
506
+ .with($group_()(sub(id, root().of('v')), mapExact(args, v => v.group)))
507
+ .with(replaceWith(field(addGrp(mapExact(args, (_, k) => $getField(root().expr(), k)))))).stages;
508
+ };
509
+
510
+ const subUpdater = (a, f) => ({ raw: (g) => a.raw(g.with(f)) });
511
+ const set = () => (fields) => ({
512
+ raw: f => {
513
+ return Object.entries(mapExactToObject0(fields, v => v)).flatMap(([k, v]) => v.raw(f).map(([l, v]) => [`.${k}${l}`, v]));
514
+ },
515
+ });
516
+ const weaken = (updater) => ({ raw: f => updater.raw(f) });
517
+ const to = (expr) => ({
518
+ raw: f => [['', expr.raw(f).get()]],
519
+ });
520
+
521
+ const dbcoll = (x) => ({
522
+ db: x.dbName,
523
+ coll: x.collectionName,
524
+ });
525
+
526
+ const $merge_ = ({ into, on, whenNotMatched, ...notMatched }) => asStages([
527
+ {
528
+ $merge: {
529
+ into: dbcoll(into),
530
+ on: on.str(),
531
+ ...(whenNotMatched && { whenNotMatched }),
532
+ ...(notMatched.stages && {
533
+ whenMatched: notMatched.whenMatched,
534
+ ...(notMatched.stages === 'ctx' && { let: rawVars(notMatched.vars, root()) }),
535
+ }),
536
+ },
537
+ },
538
+ ]);
539
+
540
+ const subMerge = (args, out, gid, extra, idPrefix) => {
541
+ const doubleReplace = (x) => x;
542
+ const mergeAggregates = $set_(set()(mapExact0(args, (v, k) => to(v.merge(root().of(k).expr(), ctx()('new').of(k).expr())))));
543
+ const gidPath = root().of(gid).expr();
544
+ const mapId = (k, v) => map1(k, v);
545
+ const F1 = {
546
+ _id: ['_id', to(idPrefix ? concat$2(val(idPrefix), $rand) : $rand)],
547
+ touchedAt: ['touchedAt', to(current)],
548
+ };
549
+ const F2 = mapId(gid, to(gidPath));
550
+ const addExtraAndMerge = {
551
+ ...mapExact0(extra, to),
552
+ ...F1,
553
+ ...F2,
554
+ };
555
+ const addTSAndExtra = {
556
+ ...mapExact0(extra, to),
557
+ touchedAt: ['touchedAt', to(current)],
558
+ };
559
+ const updater = set()(addTSAndExtra);
560
+ return (link()
561
+ .with($set_(set()(addExtraAndMerge)))
562
+ .with($merge_({
563
+ ...out,
564
+ vars: { new: ['new', root().expr()] },
565
+ stages: 'ctx',
566
+ on: root().of(gid),
567
+ whenMatched: link()
568
+ .with(mergeAggregates)
569
+ .with(doubleReplace($set_(updater))).stages,
570
+ })).stages);
571
+ };
572
+
573
+ const addGrp = (gid) => (expr) => {
574
+ const omit = omitPick();
575
+ return omit.backward(mergeExpr(omit.forward(expr), map1(gid, root().of('_id').expr())));
576
+ };
577
+ const $groupMerge = (id, args, out, gid, extra, idPrefix = '') => {
578
+ return link()
579
+ .with(subGroup(id, args, addGrp(gid)))
580
+ .with(subMerge(args, out, gid, extra, idPrefix)).stages;
581
+ };
582
+ const $groupId = (id, args, out, extra) => $groupMerge(id, args, { into: out, whenNotMatched: 'fail' }, '_id', extra);
583
+ const $group = (id, args, out, extra, idPrefix = '') => $groupMerge(id, args, { into: out, whenNotMatched: 'insert' }, '_grp', extra, idPrefix);
584
+
585
+ const $expr = (expr) => ({
586
+ raw: f => ({ $expr: expr.raw(f).get() }),
587
+ });
588
+
589
+ const $lookupRaw = ({ field1, field2 }, { coll, exec, input }, k2, k) => (f) => {
590
+ root().of('_id').expr();
591
+ root().of(k2).of('_id').expr();
592
+ return link()
593
+ .with($simpleLookup1({
594
+ coll,
595
+ k: k2,
596
+ vars: map1('local', root().with(field1).expr()),
597
+ pipeline: link()
598
+ .with(input)
599
+ .with($match_($expr(eq(ctx()('local').expr())(root().of('before').with(field2).expr()))))
600
+ .with(exec)
601
+ .with($replaceWith_(root().of('before').expr())).stages,
602
+ })(f))
603
+ .with($unwind1(k2)(f))
604
+ .with(link().stages
605
+ ).stages;
606
+ };
607
+
608
+ const deltaExpr = (expr) => (field) => {
609
+ return ite(eqTyped(root().of(field).expr(), nil), nil, expr(field));
610
+ };
611
+ const $setEach1 = (updater, dict) => {
612
+ return $set1(set()(mapExact1(dict, updater)));
613
+ };
614
+ const $setEach = (updater, dict) => $setEach1(updater, dict)(root);
615
+ const $replaceWithEach = (expr) => {
616
+ const t = deltaExpr(expr);
617
+ return $setEach(k => to(t(k)), {
618
+ after: ['after', 'after'],
619
+ before: ['before', 'before'],
620
+ });
621
+ };
622
+
623
+ const $replaceWithDelta = (expr) => $replaceWithEach((field) => sub(expr, root().of(field)));
624
+ const $setDelta = (updater) => {
625
+ const dict = {
626
+ after1: ['after1', 'after'],
627
+ before1: ['before1', 'before'],
628
+ };
629
+ const update = (k) => subUpdater(weaken(updater), root().of(k));
630
+ return link()
631
+ .with($setEach(k => to(root().of(k).expr()), dict))
632
+ .with($setEach(update, dict))
633
+ .with($replaceWithEach(field => root().of(`${field}1`).expr()))
634
+ .with($setEach(k => to(nil), dict)).stages;
635
+ };
636
+
637
+ const $unwindDelta = (k1, k2, k) => {
638
+ const stages = link()
639
+ .with(asStages([
640
+ {
641
+ $set: {
642
+ [k1]: {
643
+ before: { $ifNull: [`$before.${k1}`, null] },
644
+ after: { $ifNull: [`$after.${k1}`, null] },
645
+ },
646
+ [k2]: {
647
+ $concatArrays: [
648
+ {
649
+ $map: {
650
+ input: { $ifNull: [`$before.${k2}`, []] },
651
+ as: 'b',
652
+ in: {
653
+ before: '$$b',
654
+ after: {
655
+ $ifNull: [
656
+ {
657
+ $first: {
658
+ $filter: {
659
+ input: `$after.${k2}`,
660
+ as: 'a',
661
+ cond: { $eq: ['$$a._id', '$$b._id'] },
662
+ },
663
+ },
664
+ },
665
+ null,
666
+ ],
667
+ },
668
+ },
669
+ },
670
+ },
671
+ {
672
+ $map: {
673
+ input: {
674
+ $filter: {
675
+ input: { $ifNull: [`$after.${k2}`, []] },
676
+ as: 'a',
677
+ cond: {
678
+ $not: {
679
+ $in: [
680
+ '$$a._id',
681
+ {
682
+ $map: {
683
+ input: { $ifNull: [`$before.${k2}`, []] },
684
+ as: 'b',
685
+ in: '$$b._id',
686
+ },
687
+ },
688
+ ],
689
+ },
690
+ },
691
+ },
692
+ },
693
+ as: 'a',
694
+ in: { before: null, after: '$$a' },
695
+ },
696
+ },
697
+ ],
698
+ },
699
+ },
700
+ },
701
+ ]))
702
+ .with($unwind_(k2))
703
+ .with(asStages([
704
+ {
705
+ $replaceWith: {
706
+ _id: "$_id",
707
+ before: {
708
+ $cond: {
709
+ if: { $or: [{ $eq: [`$${k1}.before`, null] }, { $eq: [`$${k2}.before`, null] }] },
710
+ then: null,
711
+ else: {
712
+ _id: {
713
+ $concat: k
714
+ ? `$${k}.before._id`
715
+ : [
716
+ `$${[k1, k2].sort()[0]}.before._id`,
717
+ '.',
718
+ `$${[k1, k2].sort()[1]}.before._id`,
719
+ ],
720
+ },
721
+ [k1]: `$${k1}.before`,
722
+ [k2]: `$${k2}.before`,
723
+ },
724
+ },
725
+ },
726
+ after: {
727
+ $cond: {
728
+ if: { $or: [{ $eq: [`$${k1}.after`, null] }, { $eq: [`$${k2}.after`, null] }] },
729
+ then: null,
730
+ else: {
731
+ _id: {
732
+ $concat: k
733
+ ? `$${k}.after._id`
734
+ : [
735
+ `$${[k1, k2].sort()[0]}.after._id`,
736
+ '.',
737
+ `$${[k1, k2].sort()[1]}.after._id`,
738
+ ],
739
+ },
740
+ [k1]: `$${k1}.after`,
741
+ [k2]: `$${k2}.after`,
742
+ },
743
+ },
744
+ },
745
+ },
746
+ },
747
+ ])).stages;
748
+ return stages;
749
+ };
750
+
751
+ const $unwind = (k, dict) => ({
752
+ delta: link()
753
+ .with($replaceWithDelta(field({
754
+ left: ['left', root().expr()],
755
+ right: ['right', root().of(k).expr()],
756
+ })))
757
+ .with($unwindDelta('left', 'right', false))
758
+ .with($replaceWithDelta(mergeObjects(root().of('left').expr(), fieldM({
759
+ key: root().of('right').expr(),
760
+ }, dict)))).stages,
761
+ raw: $unwind1(k),
762
+ });
763
+
764
+ const $lookupDelta = ({ field1, field2 }, { coll, exec, input }, k1, k2, k) => {
765
+ return link()
766
+ .with($replaceWithDelta(field(map1(k1, root().expr()))))
767
+ .with($simpleLookup_({
768
+ coll,
769
+ k: 'a',
770
+ fields: {
771
+ foreign: root().of('before').with(field2),
772
+ local: root().of('after').of(k1).with(field1),
773
+ },
774
+ vars: {},
775
+ pipeline: link().with(input).with(exec).stages,
776
+ }))
777
+ .with($simpleLookup_({
778
+ coll,
779
+ k: 'b',
780
+ fields: {
781
+ foreign: root().of('before').with(field2),
782
+ local: root().of('before').of(k1).with(field1),
783
+ },
784
+ vars: {},
785
+ pipeline: link().with(input).with(exec).stages,
786
+ }))
787
+ .with($replaceWithEach((f) => {
788
+ const f1 = f === 'after' ? 'a' : 'b';
789
+ const omit = omitRORec();
790
+ const a = root().of(f1).of('before').expr();
791
+ const part = root().of(f);
792
+ return ite(eq(root().of(f).expr())(nil), nil, field(omit.backward(mergeExpr(omit.forward(map1(k2, a)), map1(k1, part.of(k1).expr())))));
793
+ }))
794
+ .with($unwindDelta(k1, k2, k)).stages;
795
+ };
796
+
797
+ const asBefore = (f) => f(() => root().of('before'));
798
+
799
+ const createIndex = async (collection, indexSpec, options) => {
800
+ while (true) {
801
+ try {
802
+ await collection.createIndex(indexSpec, options);
803
+ }
804
+ catch (e) {
805
+ if (e.code == 85) {
806
+ break;
807
+ }
808
+ if (e.code == 12587) {
809
+ await new Promise(res => setTimeout(res, 300));
810
+ continue;
811
+ }
812
+ throw e;
813
+ }
814
+ break;
815
+ }
816
+ };
817
+
818
+ const patch = (x, k, v) => ({ ...x, [k]: v });
819
+ const restart = (sources) => {
820
+ return map(sources, x => x.stop());
821
+ };
822
+ const race = async (sources) => {
823
+ const promises = Object.values(map(sources, ({ next }, key) => next.then(frame => ({ key, sources, frame }))));
824
+ return Promise.race(promises);
825
+ };
826
+
827
+ const nextWinner = async (previousWinner, previousWinnerNextFrame, sources, interrupt) => {
828
+ const { frame: previousFrame, key } = previousWinner;
829
+ if (!interrupt?.(key) && previousFrame.info.job) {
830
+ const previousFrameSuccessor = await previousWinnerNextFrame;
831
+ if (previousFrame.info.job === previousFrameSuccessor.info.job) {
832
+ return { frame: previousFrameSuccessor, key };
833
+ }
834
+ }
835
+ return race(sources);
836
+ };
837
+
838
+ const mergeIterators = (params) => {
839
+ const { sources, interrupt, select = race } = params;
840
+ const reiterate = (winner) => {
841
+ const { frame, key } = winner, result = frame.cont();
842
+ return {
843
+ cont: () => mergeIterators({
844
+ sources: patch(sources, key, result),
845
+ interrupt,
846
+ select: sources => nextWinner(winner, result.next, sources, interrupt),
847
+ }),
848
+ data: frame.data,
849
+ info: { key, value: frame.info, job: frame.info.job },
850
+ };
851
+ };
852
+ return {
853
+ stop: () => mergeIterators({ sources: restart(sources), interrupt }),
854
+ next: select(sources).then(reiterate),
855
+ clear: async () => {
856
+ for (const key in sources) {
857
+ await sources[key].clear();
858
+ }
859
+ },
860
+ };
861
+ };
862
+
863
+ const firstWorksMerge = (iters) => {
864
+ const iterator = () => {
865
+ const results = iters.map(iter => iter());
866
+ const sources = { ...results };
867
+ return mergeIterators({
868
+ sources,
869
+ interrupt: key => false,
870
+ });
871
+ };
872
+ return iterator;
873
+ };
874
+
875
+ class Machine {
876
+ sources;
877
+ constructor(root) {
878
+ this.sources = root ? [root] : [];
879
+ }
880
+ add(x) {
881
+ this.sources.push(x);
882
+ }
883
+ runner() {
884
+ const items = this.sources.filter(x => x);
885
+ if (items.length === 1) {
886
+ return items[0];
887
+ }
888
+ return firstWorksMerge(items);
889
+ }
890
+ start(cb) {
891
+ const run = this.runner();
892
+ return runCont(run(), cb);
893
+ }
894
+ }
895
+ const wrap = (root) => new Machine(root.runner());
896
+ const runCont = async ({ next }, cb) => {
897
+ const { cont, info } = await next;
898
+ const stopped = cb?.(info);
899
+ const it = cont();
900
+ if (stopped) {
901
+ await it.clear();
902
+ throw new Error('Machine stopped');
903
+ }
904
+ return runCont(it, cb);
905
+ };
906
+
907
+ const merge = ({ lsource: L, rsource: R, }) => mergeIterators({ sources: { L, R } });
908
+ const join = ({ lField, rField, left, right, as }, leftSnapshot, rightSnapshot, stagesUntilNextLookup) => {
909
+ createIndex(leftSnapshot.coll, { [lField.str()]: 1 }).catch(e => e.code == 86 || Promise.reject(e));
910
+ createIndex(rightSnapshot.coll, { [rField.str()]: 1 }).catch(e => e.code == 86 || Promise.reject(e));
911
+ const rightJoinField = { field1: lField, field2: rField };
912
+ const joinId = 'left';
913
+ const joinR_Snapshot = asBefore($lookupRaw(rightJoinField, rightSnapshot, as));
914
+ const resultingSnapshot = concatTStages(leftSnapshot, joinR_Snapshot);
915
+ const dict = { [as]: 'a' };
916
+ const idB = { _id: 'b' };
917
+ const dictId = { ...dict, ...idB };
918
+ return {
919
+ stages: consume => consume(concatTStages(resultingSnapshot, asBefore(stagesUntilNextLookup.raw))),
920
+ out: (finalInput) => {
921
+ const leftJoinField = { field1: rField, field2: lField };
922
+ const joinL_Delta = $lookupDelta(leftJoinField, leftSnapshot, 'right', 'left', joinId);
923
+ const joinR_Delta = $lookupDelta(rightJoinField, rightSnapshot, 'left', 'right', joinId);
924
+ const mergeForeignIntoDoc = concatStages($replaceWithDelta(mergeObjects(root().of('left').expr(), fieldM({ a: root().of('right').expr(), b: root().of('_id').expr() }, dictId))), stagesUntilNextLookup.delta);
925
+ const lRunnerInput = concatStages(joinR_Delta, mergeForeignIntoDoc);
926
+ const rRunnerInput = concatStages(joinL_Delta, mergeForeignIntoDoc);
927
+ const lRunner = left.out(concatStages(lRunnerInput, finalInput));
928
+ const rRunner = right.out(concatStages(rRunnerInput, finalInput));
929
+ return () => merge({ lsource: lRunner(), rsource: rRunner() });
930
+ },
931
+ };
932
+ };
933
+ const $lookup1 = (p) => (input) => p.left.stages((lStages) => p.right.stages((rStages) => join(p, lStages, rStages, input)));
934
+ const $lookup = (p) => (l) => $lookup1({
935
+ right: p.from,
936
+ as: p.as,
937
+ lField: p.localField,
938
+ rField: p.foreignField,
939
+ left: l(emptyDelta()),
940
+ });
941
+
942
+ const operator = () => (op) => (operand) => {
943
+ return {
944
+ raw: { [op]: operand },
945
+ expr: (field) => asExpr({
946
+ raw: f => asExprRaw({ [op]: [$ifNull(field.expr(), nil).raw(f).get(), operand] }),
947
+ }),
948
+ };
949
+ };
950
+
951
+ const dualEq = operator();
952
+ const $eq = dualEq('$eq');
953
+ const $ne = dualEq('$ne');
954
+ const comp = operator();
955
+ const $gt = comp('$gt');
956
+ const $gtTs = comp('$gt');
957
+ const $gteTs = comp('$gte');
958
+ const $lt = comp('$lt');
959
+ const dateLt = comp('$lt');
960
+ const $gte = comp('$gte');
961
+ const $lte = comp('$lte');
962
+
963
+ const dualIn = operator();
964
+ const $in = dualIn('$in');
965
+ const $nin = dualIn('$nin');
966
+
967
+ const make = (alter) => {
968
+ return (op, args) => alter(op, args.filter(defined));
969
+ };
970
+ const combine = (op, make) => (...args) => {
971
+ const q = make(op, args);
972
+ return q && { raw: field => q(x => x.raw(field)) };
973
+ };
974
+ const all = (op, x) => (x.length === 0 ? undefined : f => ({ [op]: x.map(f) }));
975
+ const first = (op, x) => (x.length === 1 ? f => f(x[0]) : all(op, x));
976
+ const $and = combine('$and', make(first));
977
+ const $nor = combine('$nor', make(all));
978
+ const $or = combine('$or', make(first));
979
+
980
+ const $matchDelta = (query) => concatStages($replaceWithDelta(ite(query, root().expr(), nil)), $match_($or(root().of('after').has($ne(null)), root().of('before').has($ne(null)))));
981
+
982
+ const $match = (query) => ({
983
+ raw: $match1($expr(query)),
984
+ delta: $matchDelta(query),
985
+ });
986
+
987
+ const $setCore = (updater) => ({
988
+ delta: $setDelta(updater),
989
+ raw: $set1(updater),
990
+ lin: $set_(updater),
991
+ });
992
+ const $set = () => (fields) => {
993
+ return $setCore(set()(fields));
994
+ };
995
+ const $replaceWith = (expr) => ({
996
+ delta: $replaceWithDelta(expr),
997
+ raw: $replaceWith1(expr),
998
+ lin: $replaceWith_(expr),
999
+ });
1000
+
1001
+ const $merge = () => (out, keys) => {
1002
+ const omRORec = omitRORec();
1003
+ const patch = mapExactToObject(keys, (_, k) => [k, root().of('after').of(k).expr()]);
1004
+ const replacer = ite(eqTyped(root().of('after').expr(), nil), field(omRORec.backward(spread(mapExact(keys, () => nil), {
1005
+ _id: ['_id', root().of('_id').expr()],
1006
+ touchedAt: ['touchedAt', current],
1007
+ }))), field(omitPick().backward(spread(patch, {
1008
+ _id: ['_id', root().of('after').of('_id').expr()],
1009
+ touchedAt: ['touchedAt', current],
1010
+ }))));
1011
+ return link()
1012
+ .with($replaceWith_(replacer))
1013
+ .with($merge_({
1014
+ into: out,
1015
+ on: root().of('_id'),
1016
+ whenNotMatched: 'fail',
1017
+ whenMatched: 'merge',
1018
+ })).stages;
1019
+ };
1020
+
1021
+ const $upsert = (out) => {
1022
+ const replacer = ite(eqTyped(root().of('after').expr(), nil), field({
1023
+ deletedAt: ['deletedAt', current],
1024
+ _id: ['_id', root().of('_id').expr()],
1025
+ touchedAt: ['touchedAt', current],
1026
+ }), mergeObjects(root().of('after').expr(), field({ deletedAt: ['deletedAt', nil], touchedAt: ['touchedAt', current] })));
1027
+ return link()
1028
+ .with($replaceWith_(replacer))
1029
+ .with($merge_({
1030
+ into: out,
1031
+ on: root().of('_id'),
1032
+ whenMatched: 'merge',
1033
+ whenNotMatched: 'insert',
1034
+ })).stages;
1035
+ };
1036
+
1037
+ const T = (s) => `Timestamp(${parseInt(`${BigInt(s) / 2n ** 32n}`)}, ${parseInt(`${BigInt(s) % 2n ** 32n}`)})`;
1038
+ const replace = (s) => s.replace(/\{"\$timestamp":"(\d+)"\}/g, (_, d) => T(d));
1039
+ const json = (a) => replace(JSON.stringify(a));
1040
+ const log = (...args) => console.log(new Date(), ...args.map(a => (typeof a === 'function' ? a(replace) : a && typeof a === 'object' ? json(a) : a)));
1041
+
1042
+ const aggregate = (input, snapshot = true, start = Date.now()) => input(({ coll, input }) => {
1043
+ const req = {
1044
+ aggregate: coll.collectionName,
1045
+ pipeline: input,
1046
+ cursor: {},
1047
+ ...(snapshot && { readConcern: { level: 'snapshot' } }),
1048
+ };
1049
+ log('exec', req);
1050
+ return coll.s.db.command(req).then(result => {
1051
+ log('execed', req, result, 'took', Date.now() - start);
1052
+ return result;
1053
+ }, err => {
1054
+ log('err', req);
1055
+ throw new Error(err);
1056
+ });
1057
+ });
1058
+
1059
+ const addTeardown = (it, tr) => {
1060
+ if (!tr)
1061
+ return it;
1062
+ return () => {
1063
+ const { next, stop, clear } = it();
1064
+ const n = next;
1065
+ return {
1066
+ next: n.then(({ cont, ...res }) => ({ cont: addTeardown(cont, tr), ...res })),
1067
+ stop: () => (tr(), stop()),
1068
+ clear: async () => {
1069
+ tr();
1070
+ await clear();
1071
+ },
1072
+ };
1073
+ };
1074
+ };
1075
+
1076
+ const changeKeys = ['fullDocument', 'fullDocumentBeforeChange'];
1077
+ const subQ = (a, f) => ({ raw: g => a.raw(g.with(f)) });
1078
+ const makeWatchStream = (db, { collection, projection: p, hardMatch: m }, startAt) => {
1079
+ const projection = mapExactToObject(p, v => v);
1080
+ const pipeline = [];
1081
+ if (m) {
1082
+ const q = $or(...changeKeys.map((k) => subQ(m, root().of(k))));
1083
+ if (q)
1084
+ pipeline.push({
1085
+ $match: { $or: [q.raw(root()), Object.fromEntries(changeKeys.map(k => [k, null]))] },
1086
+ });
1087
+ }
1088
+ pipeline.push({
1089
+ $project: {
1090
+ _id: 1,
1091
+ fullDocument: projection,
1092
+ fullDocumentBeforeChange: projection,
1093
+ documentKey: 1,
1094
+ clusterTime: 1,
1095
+ },
1096
+ });
1097
+ pipeline.push({
1098
+ $match: {
1099
+ $or: [
1100
+ { $expr: { $ne: ['$fullDocument', '$fullDocumentBeforeChange'] } },
1101
+ Object.fromEntries(changeKeys.map(k => [k, null])),
1102
+ ],
1103
+ },
1104
+ });
1105
+ const stream = db.collection(collection.collectionName).watch(pipeline, {
1106
+ fullDocument: 'required',
1107
+ fullDocumentBeforeChange: 'required',
1108
+ startAtOperationTime: startAt,
1109
+ });
1110
+ const tryNext = async () => {
1111
+ const doc = await stream.tryNext();
1112
+ if (doc)
1113
+ await new Promise(resolve => setTimeout(resolve, 100));
1114
+ return doc;
1115
+ };
1116
+ return { tryNext, close: () => stream.close() };
1117
+ };
1118
+
1119
+ const streamNames = {};
1120
+ const executes$1 = (view, input, streamName) => {
1121
+ const hash = crypto
1122
+ .createHash('md5')
1123
+ .update(new Error().stack + '')
1124
+ .digest('base64url');
1125
+ if (!streamNames[streamName])
1126
+ streamNames[streamName] = hash;
1127
+ else if (streamNames[streamName] != hash)
1128
+ throw new Error(`streamName ${streamName} already used`);
1129
+ const { collection, projection, hardMatch, match } = view;
1130
+ const job = {};
1131
+ const db = collection.s.db, coll = collection.collectionName;
1132
+ db.command({
1133
+ collMod: coll,
1134
+ changeStreamPreAndPostImages: { enabled: true },
1135
+ });
1136
+ createIndex(collection, { touchedAt: 1 }, hardMatch
1137
+ ? {
1138
+ partialFilterExpression: hardMatch,
1139
+ name: 'touchedAt_hard_' + new mongodb.UUID().toString('base64'),
1140
+ }
1141
+ : {}).catch(e => e.code == 86 || Promise.reject(e));
1142
+ const last = db.collection('__last');
1143
+ const snapshotCollection = db.collection(coll + '_' + streamName + '_snapshot');
1144
+ createIndex(snapshotCollection, { updated: 1 }, {
1145
+ partialFilterExpression: { updated: true },
1146
+ name: 'updated_' + new mongodb.UUID().toString('base64'),
1147
+ });
1148
+ createIndex(snapshotCollection, { updated: 1 }, {
1149
+ partialFilterExpression: { updated: true, after: null, before: null },
1150
+ name: 'updated_nulls_' + new mongodb.UUID().toString('base64'),
1151
+ });
1152
+ const projectInput = $project_(spread(projection, {
1153
+ deletedAt: ['deletedAt', 1],
1154
+ _id: ['_id', 1],
1155
+ }));
1156
+ const run = (finalInput) => {
1157
+ const clear = async () => { };
1158
+ const withStop = (next, tr) => {
1159
+ return addTeardown(() => ({ stop, next: next(), clear }), tr);
1160
+ };
1161
+ const next = (next, debug, tr) => ({
1162
+ cont: withStop(next, tr),
1163
+ data: [],
1164
+ info: { job, debug },
1165
+ });
1166
+ const step0 = () => Promise.resolve(next(step1, 'empty new collection'));
1167
+ const stop = withStop(step0);
1168
+ const step1 = async () => {
1169
+ await snapshotCollection.updateMany({ updated: true }, { $set: { updated: false, after: null } });
1170
+ return next(step2, 'get last update');
1171
+ };
1172
+ const step2 = () => last.findOne({ _id: streamName }).then(ts => next(step3(ts), 'clone into new collection'));
1173
+ const step3 = (lastTS) => async () => {
1174
+ const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(lastTS.ts)), hardMatch);
1175
+ const notDeleted = eq($ifNull(root().of('deletedAt').expr(), nil))(nil);
1176
+ const query = match ? and(notDeleted, match) : notDeleted;
1177
+ const replaceRaw = $replaceWith_(field({
1178
+ after: ['after', ite(query, root().expr(), nil)],
1179
+ updated: ['updated', val(true)],
1180
+ _id: ['_id', root().of('_id').expr()],
1181
+ }));
1182
+ const cloneIntoNew = link()
1183
+ .with($match_(hardQuery))
1184
+ .with(projectInput)
1185
+ .with(replaceRaw)
1186
+ .with($merge_({
1187
+ into: snapshotCollection,
1188
+ on: root().of('_id'),
1189
+ whenMatched: 'merge',
1190
+ whenNotMatched: 'insert',
1191
+ })).stages;
1192
+ const r = await aggregate(c => c({ coll: collection, input: cloneIntoNew }));
1193
+ await snapshotCollection.deleteMany({ updated: true, after: null, before: null });
1194
+ return next(step4(r), 'run the aggregation');
1195
+ };
1196
+ const makeStream = (startAt) => makeWatchStream(db, view, startAt);
1197
+ const step4 = (result) => async () => {
1198
+ const start = Date.now();
1199
+ await snapshotCollection.updateMany({ before: null }, { $set: { before: null } });
1200
+ const aggResult = await aggregate(c => c({
1201
+ coll: snapshotCollection,
1202
+ input: link()
1203
+ .with($match_(root().of('updated').has($eq(true))))
1204
+ .with($match_($expr(ne(root().of('after').expr())(root().of('before').expr()))))
1205
+ .with(input.delta)
1206
+ .with(finalInput).stages,
1207
+ }), false, start);
1208
+ const stream = makeStream(result.cursor.atClusterTime);
1209
+ return next(step5({ result, aggResult, stream }), 'remove handled deleted updated', () => stream.close());
1210
+ };
1211
+ const step5 = (l) => async () => {
1212
+ log(`remove handled deleted updated db['${snapshotCollection.collectionName}'].deleteMany({ updated: true, after: null })`);
1213
+ await snapshotCollection.deleteMany({ updated: true, after: null });
1214
+ log('removed handled deleted updated');
1215
+ return next(step6(l), 'update snapshot aggregation');
1216
+ };
1217
+ const step6 = (l) => async () => {
1218
+ log('update snapshot aggregation', `db['${snapshotCollection.collectionName}'].updateMany({ updated: true }, [ { $set: { updated: false, after: null, before: '$after' } } ])`);
1219
+ await snapshotCollection.updateMany({ updated: true }, [
1220
+ {
1221
+ $set: {
1222
+ updated: false,
1223
+ after: null,
1224
+ before: '$after',
1225
+ },
1226
+ },
1227
+ ]);
1228
+ log('updated snapshot aggregation');
1229
+ return next(step7(l), 'update __last');
1230
+ };
1231
+ const step7 = (l) => async () => {
1232
+ await last.updateOne({ _id: streamName }, { $set: { ts: l.result.cursor.atClusterTime } }, { upsert: true });
1233
+ return step8(l);
1234
+ };
1235
+ const step8 = (l) => {
1236
+ return {
1237
+ data: l.aggResult.cursor.firstBatch,
1238
+ info: { job: undefined, debug: 'wait for change' },
1239
+ cont: withStop(() => l.stream.tryNext().then(doc => (doc ? next(step2, 'restart') : step8(l)))),
1240
+ };
1241
+ };
1242
+ return stop;
1243
+ };
1244
+ const hasBefore = root().of('before').has($ne(null));
1245
+ return {
1246
+ stages: c => c({
1247
+ coll: snapshotCollection,
1248
+ input: $match_(hasBefore),
1249
+ exec: asBefore(input.raw),
1250
+ }),
1251
+ out: run,
1252
+ };
1253
+ };
1254
+ const staging = (view, streamName) => pipe(input => executes$1(view, input, streamName), emptyDelta(), concatDelta, emptyDelta);
1255
+
1256
+ const executes = (view, input, streamName) => {
1257
+ const hash = crypto
1258
+ .createHash('md5')
1259
+ .update(new Error().stack + '')
1260
+ .digest('base64url');
1261
+ if (!streamNames[streamName])
1262
+ streamNames[streamName] = hash;
1263
+ else if (streamNames[streamName] != hash)
1264
+ throw new Error('streamName already used');
1265
+ const { collection, projection, hardMatch, match } = view;
1266
+ const job = {};
1267
+ const db = collection.s.db, coll = collection.collectionName;
1268
+ db.command({
1269
+ collMod: coll,
1270
+ changeStreamPreAndPostImages: { enabled: true },
1271
+ });
1272
+ createIndex(collection, { touchedAt: 1 }, {
1273
+ partialFilterExpression: { deletedAt: { $eq: null } },
1274
+ name: 'touchedAt_' + new mongodb.UUID().toString('base64'),
1275
+ });
1276
+ const last = db.collection('__last');
1277
+ const projectInput = $project_(spread(projection, {
1278
+ deletedAt: ['deletedAt', 1],
1279
+ _id: ['_id', 1],
1280
+ }));
1281
+ const notDeleted = root().of('deletedAt').has($eq(null));
1282
+ const run = (finalInput) => {
1283
+ const clear = async () => { };
1284
+ const withStop = (next, tr) => {
1285
+ return addTeardown(() => ({ stop, next: next(), clear }), tr);
1286
+ };
1287
+ const next = (next, debug, tr) => ({
1288
+ cont: withStop(next, tr),
1289
+ data: [],
1290
+ info: { job, debug },
1291
+ });
1292
+ const step0 = () => Promise.resolve(next(step1, 'get last update'));
1293
+ const stop = withStop(step0);
1294
+ const step1 = () => last.findOne({ _id: streamName }).then(ts => next(step4(ts), 'clone into new collection'));
1295
+ const makeStream = (startAt) => makeWatchStream(db, view, startAt);
1296
+ const step4 = (lastTS) => async () => {
1297
+ const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(lastTS.ts)), hardMatch, notDeleted, match && $expr(match));
1298
+ const aggResult = await aggregate(c => c({
1299
+ coll: collection,
1300
+ input: link()
1301
+ .with($match_(hardQuery))
1302
+ .with(projectInput)
1303
+ .with(input)
1304
+ .with(finalInput).stages,
1305
+ }));
1306
+ const stream = makeStream(aggResult.cursor.atClusterTime);
1307
+ return next(step7({ aggResult, result: aggResult, stream }), 'update __last', () => stream.close());
1308
+ };
1309
+ const step7 = (l) => async () => {
1310
+ await last.updateOne({ _id: streamName }, { $set: { ts: l.result.cursor.atClusterTime } }, { upsert: true });
1311
+ return step8(l);
1312
+ };
1313
+ const step8 = (l) => {
1314
+ return {
1315
+ data: l.aggResult.cursor.firstBatch,
1316
+ info: { job: undefined, debug: 'wait for change' },
1317
+ cont: withStop(() => l.stream.tryNext().then(doc => (doc ? next(step1, 'restart') : step8(l)))),
1318
+ };
1319
+ };
1320
+ return stop;
1321
+ };
1322
+ return {
1323
+ out: run,
1324
+ };
1325
+ };
1326
+ const emptyLin = () => ({ lin: link().stages });
1327
+ const from = (view, streamName) => pipe(input => executes(view, input.lin, streamName), { lin: link().stages }, ({ lin: a }, { lin: b }) => ({ lin: concatStages(a, b) }), emptyLin);
1328
+
1329
+ const dayAndMonthPart = (date) => asExpr({
1330
+ raw: f => asExprRaw({ $dateToString: { date: date.raw(f).get(), format: '%m-%d' } }),
1331
+ });
1332
+ const now = () => asExpr({
1333
+ raw: f => asExprRaw('$$NOW'),
1334
+ });
1335
+ const monthPart = (date) => asExpr({
1336
+ raw: f => asExprRaw({ $dateToString: { date: date.raw(f).get(), format: '%Y-%m' } }),
1337
+ });
1338
+ const dateAdd = (date, amount, unit) => asExpr({
1339
+ raw: f => asExprRaw({
1340
+ $dateAdd: {
1341
+ startDate: date.raw(f).get(),
1342
+ unit: unit.raw(f).get(),
1343
+ amount: amount.raw(f).get(),
1344
+ },
1345
+ }),
1346
+ });
1347
+ const maxDate = (expr) => asExpr({
1348
+ raw: f => asExprRaw({ $max: expr.raw(f).get() }),
1349
+ });
1350
+ const minDate = (expr) => asExpr({
1351
+ raw: f => asExprRaw({ $min: expr.raw(f).get() }),
1352
+ });
1353
+ const datePart = (date) => asExpr({
1354
+ raw: f => asExprRaw({ $dateToString: { date: date.raw(f).get(), format: '%Y-%m-%d' } }),
1355
+ });
1356
+ const startOf = (startDate, freq, offset) => asExpr({
1357
+ raw: f => asExprRaw({
1358
+ $let: {
1359
+ vars: { d: startDate.raw(f).get(), f: freq.raw(f).get(), i: offset.raw(f).get() },
1360
+ in: {
1361
+ $switch: {
1362
+ branches: [
1363
+ {
1364
+ case: { $eq: ['$$f', 'week'] },
1365
+ then: {
1366
+ $dateFromParts: {
1367
+ isoWeekYear: { $isoWeekYear: '$$d' },
1368
+ isoWeek: {
1369
+ $add: [
1370
+ { $isoWeek: '$$d' },
1371
+ {
1372
+ $cond: {
1373
+ if: {
1374
+ $lt: ['$$i', { $isoDayOfWeek: '$$d' }],
1375
+ },
1376
+ then: 1,
1377
+ else: 0,
1378
+ },
1379
+ },
1380
+ ],
1381
+ },
1382
+ isoDayOfWeek: '$$i',
1383
+ },
1384
+ },
1385
+ },
1386
+ {
1387
+ case: { $eq: ['$$f', 'month'] },
1388
+ then: {
1389
+ $dateFromParts: {
1390
+ year: { $year: '$$d' },
1391
+ month: {
1392
+ $add: [
1393
+ { $month: '$$d' },
1394
+ {
1395
+ $cond: {
1396
+ if: {
1397
+ $lt: ['$$i', { $dayOfMonth: '$$d' }],
1398
+ },
1399
+ then: 1,
1400
+ else: 0,
1401
+ },
1402
+ },
1403
+ ],
1404
+ },
1405
+ day: '$$i',
1406
+ },
1407
+ },
1408
+ },
1409
+ {
1410
+ case: { $eq: ['$$f', 'year'] },
1411
+ then: {
1412
+ $dateFromParts: {
1413
+ year: {
1414
+ $add: [
1415
+ { $year: '$$d' },
1416
+ {
1417
+ $cond: {
1418
+ if: {
1419
+ $or: [
1420
+ {
1421
+ $lt: [{ $floor: { $divide: ['$$i', 100] } }, { $month: '$$d' }],
1422
+ },
1423
+ {
1424
+ $and: [
1425
+ {
1426
+ $eq: [
1427
+ { $floor: { $divide: ['$$i', 100] } },
1428
+ { $month: '$$d' },
1429
+ ],
1430
+ },
1431
+ {
1432
+ $lt: [{ $mod: ['$$i', 100] }, { $dayOfMonth: '$$d' }],
1433
+ },
1434
+ ],
1435
+ },
1436
+ ],
1437
+ },
1438
+ then: 1,
1439
+ else: 0,
1440
+ },
1441
+ },
1442
+ ],
1443
+ },
1444
+ month: { $floor: { $divide: ['$$i', 100] } },
1445
+ day: { $mod: ['$$i', 100] },
1446
+ },
1447
+ },
1448
+ },
1449
+ ],
1450
+ default: {
1451
+ $dateFromParts: {
1452
+ year: { $year: '$$d' },
1453
+ month: { $month: '$$d' },
1454
+ day: { $add: [{ $dayOfMonth: '$$d' }, '$$i'] },
1455
+ },
1456
+ },
1457
+ },
1458
+ },
1459
+ },
1460
+ }),
1461
+ });
1462
+ const dateDiff = ({ end, unit, start, }) => asExpr({
1463
+ raw: f => asExprRaw({
1464
+ $let: {
1465
+ vars: {
1466
+ f: unit.raw(f).get(),
1467
+ end: end.raw(f).get(),
1468
+ start: start.raw(f).get(),
1469
+ },
1470
+ in: {
1471
+ $let: {
1472
+ vars: {
1473
+ diff: {
1474
+ $dateDiff: {
1475
+ startDate: '$$start',
1476
+ endDate: '$$end',
1477
+ unit: '$$f',
1478
+ },
1479
+ },
1480
+ },
1481
+ in: {
1482
+ $switch: {
1483
+ branches: [
1484
+ {
1485
+ case: { $eq: ['$$f', 'month'] },
1486
+ then: {
1487
+ $cond: {
1488
+ if: {
1489
+ $lte: [{ $dayOfMonth: '$$start' }, { $dayOfMonth: '$$end' }],
1490
+ },
1491
+ then: '$$diff',
1492
+ else: { $subtract: ['$$diff', 1] },
1493
+ },
1494
+ },
1495
+ },
1496
+ {
1497
+ case: { $eq: ['$$f', 'week'] },
1498
+ then: {
1499
+ $cond: {
1500
+ if: {
1501
+ $lte: [{ $isoDayOfWeek: '$$start' }, { $isoDayOfWeek: '$$end' }],
1502
+ },
1503
+ then: '$$diff',
1504
+ else: { $subtract: ['$$diff', 1] },
1505
+ },
1506
+ },
1507
+ },
1508
+ {
1509
+ case: { $eq: ['$$f', 'year'] },
1510
+ then: {
1511
+ $cond: {
1512
+ if: {
1513
+ $or: [
1514
+ {
1515
+ $lt: [{ $month: '$$start' }, { $month: '$$end' }],
1516
+ },
1517
+ {
1518
+ $and: [
1519
+ {
1520
+ $eq: [{ $month: '$$start' }, { $month: '$$end' }],
1521
+ },
1522
+ {
1523
+ $lte: [{ $dayOfMonth: '$$start' }, { $dayOfMonth: '$$end' }],
1524
+ },
1525
+ ],
1526
+ },
1527
+ ],
1528
+ },
1529
+ then: '$$diff',
1530
+ else: { $subtract: ['$$diff', 1] },
1531
+ },
1532
+ },
1533
+ },
1534
+ ],
1535
+ default: '$$diff',
1536
+ },
1537
+ },
1538
+ },
1539
+ },
1540
+ },
1541
+ }),
1542
+ });
1543
+
1544
+ const range = (start, end, step) => asExpr({
1545
+ raw: f => asExprRaw({ $range: [start.raw(f).get(), end.raw(f).get(), step?.raw(f).get() ?? 1] }),
1546
+ });
1547
+ const $map1 = (ex, map) => asExpr({
1548
+ raw: f => asExprRaw({
1549
+ $map: {
1550
+ input: ex.raw(f).get(),
1551
+ as: 'item',
1552
+ in: map(ctx()('item')).raw(f).get(),
1553
+ },
1554
+ }),
1555
+ });
1556
+ const $map = (ex, map) => $map1(ex, i => map(i.expr()));
1557
+
1558
+ require('dotenv').config();
1559
+ const uri = process.env['MONGO_URL'];
1560
+
1561
+ const enablePreAndPostImages = (coll) => coll.s.db.command({
1562
+ collMod: coll.collectionName,
1563
+ changeStreamPreAndPostImages: { enabled: true },
1564
+ });
1565
+ const prepare = async (testName) => {
1566
+ const client = new mongodb.MongoClient(uri, testName ? { monitorCommands: true } : {});
1567
+ if (testName) {
1568
+ const handler = (c) => {
1569
+ promises.writeFile(`./out/${testName}.log`, JSON.stringify(c.command) + ',\n', { flag: 'w' });
1570
+ };
1571
+ client.on('commandStarted', handler);
1572
+ client.on('commandSucceeded', handler);
1573
+ }
1574
+ await client.connect();
1575
+ await client.db('admin').command({
1576
+ setClusterParameter: {
1577
+ changeStreamOptions: {
1578
+ preAndPostImages: { expireAfterSeconds: 60 },
1579
+ },
1580
+ },
1581
+ });
1582
+ return client;
1583
+ };
1584
+
1585
+ exports.$accumulator = $accumulator;
1586
+ exports.$and = $and;
1587
+ exports.$countDict = $countDict;
1588
+ exports.$entries = $entries;
1589
+ exports.$eq = $eq;
1590
+ exports.$expr = $expr;
1591
+ exports.$getField = $getField;
1592
+ exports.$group = $group;
1593
+ exports.$groupId = $groupId;
1594
+ exports.$groupMerge = $groupMerge;
1595
+ exports.$gt = $gt;
1596
+ exports.$gtTs = $gtTs;
1597
+ exports.$gte = $gte;
1598
+ exports.$gteTs = $gteTs;
1599
+ exports.$ifNull = $ifNull;
1600
+ exports.$in = $in;
1601
+ exports.$keys = $keys;
1602
+ exports.$let = $let;
1603
+ exports.$lookup = $lookup;
1604
+ exports.$lt = $lt;
1605
+ exports.$lte = $lte;
1606
+ exports.$map = $map;
1607
+ exports.$map1 = $map1;
1608
+ exports.$match = $match;
1609
+ exports.$matchDelta = $matchDelta;
1610
+ exports.$merge = $merge;
1611
+ exports.$merge_ = $merge_;
1612
+ exports.$ne = $ne;
1613
+ exports.$nin = $nin;
1614
+ exports.$nor = $nor;
1615
+ exports.$or = $or;
1616
+ exports.$pushDict = $pushDict;
1617
+ exports.$rand = $rand;
1618
+ exports.$replaceWith = $replaceWith;
1619
+ exports.$set = $set;
1620
+ exports.$sum = $sum;
1621
+ exports.$unwind = $unwind;
1622
+ exports.$unwindDelta = $unwindDelta;
1623
+ exports.$upsert = $upsert;
1624
+ exports.Field = Field;
1625
+ exports.Machine = Machine;
1626
+ exports.add = add;
1627
+ exports.and = and;
1628
+ exports.array = array;
1629
+ exports.ceil = ceil;
1630
+ exports.comp = comp;
1631
+ exports.concat = concat;
1632
+ exports.ctx = ctx;
1633
+ exports.current = current;
1634
+ exports.dateAdd = dateAdd;
1635
+ exports.dateDiff = dateDiff;
1636
+ exports.dateLt = dateLt;
1637
+ exports.datePart = datePart;
1638
+ exports.dayAndMonthPart = dayAndMonthPart;
1639
+ exports.divide = divide;
1640
+ exports.enablePreAndPostImages = enablePreAndPostImages;
1641
+ exports.eq = eq;
1642
+ exports.eqTyped = eqTyped;
1643
+ exports.except = except;
1644
+ exports.exprMapVal = exprMapVal;
1645
+ exports.filter = filter;
1646
+ exports.filterDefined = filterDefined;
1647
+ exports.first = first$1;
1648
+ exports.firstSure = firstSure;
1649
+ exports.floor = floor;
1650
+ exports.from = from;
1651
+ exports.func = func;
1652
+ exports.gt = gt;
1653
+ exports.gte = gte;
1654
+ exports.inArray = inArray;
1655
+ exports.isArray = isArray;
1656
+ exports.ite = ite;
1657
+ exports.last = last;
1658
+ exports.lt = lt;
1659
+ exports.lte = lte;
1660
+ exports.mapVal = mapVal;
1661
+ exports.max = max;
1662
+ exports.maxDate = maxDate;
1663
+ exports.mergeObjects = mergeObjects;
1664
+ exports.minDate = minDate;
1665
+ exports.monthPart = monthPart;
1666
+ exports.multiply = multiply;
1667
+ exports.ne = ne;
1668
+ exports.nil = nil;
1669
+ exports.notNull = notNull;
1670
+ exports.now = now;
1671
+ exports.prepare = prepare;
1672
+ exports.rand = rand;
1673
+ exports.range = range;
1674
+ exports.root = root;
1675
+ exports.setField = setField;
1676
+ exports.size = size;
1677
+ exports.slice = slice;
1678
+ exports.sortArray = sortArray;
1679
+ exports.staging = staging;
1680
+ exports.startOf = startOf;
1681
+ exports.sub = sub;
1682
+ exports.subtract = subtract;
1683
+ exports.val = val;
1684
+ exports.wrap = wrap;