@powersync/service-core 0.0.0-dev-20250813080357 → 0.0.0-dev-20250819134004
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/CHANGELOG.md +10 -6
- package/dist/api/diagnostics.js +1 -1
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/events/EventsEngine.d.ts +11 -0
- package/dist/{emitters/EmitterEngine.js → events/EventsEngine.js} +2 -2
- package/dist/events/EventsEngine.js.map +1 -0
- package/dist/routes/configure-fastify.d.ts +40 -0
- package/dist/routes/endpoints/socket-route.js +4 -7
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.js +1 -27
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.d.ts +80 -0
- package/dist/routes/endpoints/sync-stream.js +7 -11
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/storage/ReportStorage.d.ts +8 -0
- package/dist/storage/ReportStorage.js +2 -0
- package/dist/storage/ReportStorage.js.map +1 -0
- package/dist/storage/StorageEngine.d.ts +2 -2
- package/dist/storage/StorageEngine.js.map +1 -1
- package/dist/storage/StorageProvider.d.ts +3 -3
- package/dist/storage/storage-index.d.ts +1 -1
- package/dist/storage/storage-index.js +1 -1
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/sync/BucketChecksumState.d.ts +34 -8
- package/dist/sync/BucketChecksumState.js +153 -18
- package/dist/sync/BucketChecksumState.js.map +1 -1
- package/dist/sync/sync.d.ts +1 -2
- package/dist/sync/sync.js +8 -10
- package/dist/sync/sync.js.map +1 -1
- package/dist/system/ServiceContext.d.ts +3 -3
- package/dist/system/ServiceContext.js +7 -4
- package/dist/system/ServiceContext.js.map +1 -1
- package/dist/util/protocol-types.d.ts +153 -5
- package/dist/util/protocol-types.js +41 -2
- package/dist/util/protocol-types.js.map +1 -1
- package/package.json +6 -6
- package/src/api/diagnostics.ts +1 -1
- package/src/{emitters/EmitterEngine.ts → events/EventsEngine.ts} +5 -6
- package/src/routes/endpoints/socket-route.ts +5 -9
- package/src/routes/endpoints/sync-rules.ts +1 -28
- package/src/routes/endpoints/sync-stream.ts +8 -13
- package/src/storage/ReportStorage.ts +11 -0
- package/src/storage/StorageEngine.ts +3 -3
- package/src/storage/StorageProvider.ts +3 -4
- package/src/storage/storage-index.ts +1 -1
- package/src/sync/BucketChecksumState.ts +183 -26
- package/src/sync/sync.ts +15 -13
- package/src/system/ServiceContext.ts +8 -5
- package/src/util/protocol-types.ts +138 -5
- package/test/src/sync/BucketChecksumState.test.ts +366 -34
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/emitters/EmitterEngine.d.ts +0 -12
- package/dist/emitters/EmitterEngine.js.map +0 -1
- package/dist/emitters/emitter-interfaces.d.ts +0 -8
- package/dist/emitters/emitter-interfaces.js +0 -2
- package/dist/emitters/emitter-interfaces.js.map +0 -1
- package/dist/storage/ReportStorageFactory.d.ts +0 -8
- package/dist/storage/ReportStorageFactory.js +0 -2
- package/dist/storage/ReportStorageFactory.js.map +0 -1
- package/src/emitters/emitter-interfaces.ts +0 -12
- package/src/storage/ReportStorageFactory.ts +0 -9
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BucketChecksum,
|
|
3
3
|
BucketChecksumState,
|
|
4
|
+
BucketChecksumStateOptions,
|
|
4
5
|
BucketChecksumStateStorage,
|
|
5
6
|
CHECKPOINT_INVALIDATE_ALL,
|
|
6
7
|
ChecksumMap,
|
|
7
8
|
InternalOpId,
|
|
8
9
|
ReplicationCheckpoint,
|
|
10
|
+
StreamingSyncRequest,
|
|
9
11
|
SyncContext,
|
|
10
12
|
WatchFilterEvent
|
|
11
13
|
} from '@/index.js';
|
|
12
14
|
import { JSONBig } from '@powersync/service-jsonbig';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
+
import {
|
|
16
|
+
SqliteJsonRow,
|
|
17
|
+
ParameterLookup,
|
|
18
|
+
SqlSyncRules,
|
|
19
|
+
RequestJwtPayload,
|
|
20
|
+
BucketSource,
|
|
21
|
+
BucketSourceType,
|
|
22
|
+
BucketParameterQuerier
|
|
23
|
+
} from '@powersync/service-sync-rules';
|
|
24
|
+
import { describe, expect, test, beforeEach } from 'vitest';
|
|
15
25
|
|
|
16
26
|
describe('BucketChecksumState', () => {
|
|
17
27
|
// Single global[] bucket.
|
|
@@ -55,6 +65,9 @@ bucket_definitions:
|
|
|
55
65
|
maxDataFetchConcurrency: 10
|
|
56
66
|
});
|
|
57
67
|
|
|
68
|
+
const syncRequest: StreamingSyncRequest = {};
|
|
69
|
+
const tokenPayload: RequestJwtPayload = { sub: '' };
|
|
70
|
+
|
|
58
71
|
test('global bucket with update', async () => {
|
|
59
72
|
const storage = new MockBucketChecksumStateStorage();
|
|
60
73
|
// Set intial state
|
|
@@ -62,7 +75,8 @@ bucket_definitions:
|
|
|
62
75
|
|
|
63
76
|
const state = new BucketChecksumState({
|
|
64
77
|
syncContext,
|
|
65
|
-
|
|
78
|
+
syncRequest,
|
|
79
|
+
tokenPayload,
|
|
66
80
|
syncRules: SYNC_RULES_GLOBAL,
|
|
67
81
|
bucketStorage: storage
|
|
68
82
|
});
|
|
@@ -75,9 +89,10 @@ bucket_definitions:
|
|
|
75
89
|
line.advance();
|
|
76
90
|
expect(line.checkpointLine).toEqual({
|
|
77
91
|
checkpoint: {
|
|
78
|
-
buckets: [{ bucket: 'global[]', checksum: 1, count: 1, priority: 3 }],
|
|
92
|
+
buckets: [{ bucket: 'global[]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }],
|
|
79
93
|
last_op_id: '1',
|
|
80
|
-
write_checkpoint: undefined
|
|
94
|
+
write_checkpoint: undefined,
|
|
95
|
+
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
81
96
|
}
|
|
82
97
|
});
|
|
83
98
|
expect(line.bucketsToFetch).toEqual([
|
|
@@ -111,7 +126,7 @@ bucket_definitions:
|
|
|
111
126
|
expect(line2.checkpointLine).toEqual({
|
|
112
127
|
checkpoint_diff: {
|
|
113
128
|
removed_buckets: [],
|
|
114
|
-
updated_buckets: [{ bucket: 'global[]', checksum: 2, count: 2, priority: 3 }],
|
|
129
|
+
updated_buckets: [{ bucket: 'global[]', checksum: 2, count: 2, priority: 3, subscriptions: [{ default: 0 }] }],
|
|
115
130
|
last_op_id: '2',
|
|
116
131
|
write_checkpoint: undefined
|
|
117
132
|
}
|
|
@@ -129,9 +144,9 @@ bucket_definitions:
|
|
|
129
144
|
|
|
130
145
|
const state = new BucketChecksumState({
|
|
131
146
|
syncContext,
|
|
147
|
+
tokenPayload,
|
|
132
148
|
// Client sets the initial state here
|
|
133
|
-
|
|
134
|
-
syncParams: new RequestParameters({ sub: '' }, {}),
|
|
149
|
+
syncRequest: { buckets: [{ name: 'global[]', after: '1' }] },
|
|
135
150
|
syncRules: SYNC_RULES_GLOBAL,
|
|
136
151
|
bucketStorage: storage
|
|
137
152
|
});
|
|
@@ -144,9 +159,10 @@ bucket_definitions:
|
|
|
144
159
|
line.advance();
|
|
145
160
|
expect(line.checkpointLine).toEqual({
|
|
146
161
|
checkpoint: {
|
|
147
|
-
buckets: [{ bucket: 'global[]', checksum: 1, count: 1, priority: 3 }],
|
|
162
|
+
buckets: [{ bucket: 'global[]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }],
|
|
148
163
|
last_op_id: '1',
|
|
149
|
-
write_checkpoint: undefined
|
|
164
|
+
write_checkpoint: undefined,
|
|
165
|
+
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
150
166
|
}
|
|
151
167
|
});
|
|
152
168
|
expect(line.bucketsToFetch).toEqual([
|
|
@@ -167,7 +183,8 @@ bucket_definitions:
|
|
|
167
183
|
|
|
168
184
|
const state = new BucketChecksumState({
|
|
169
185
|
syncContext,
|
|
170
|
-
|
|
186
|
+
tokenPayload,
|
|
187
|
+
syncRequest,
|
|
171
188
|
syncRules: SYNC_RULES_GLOBAL_TWO,
|
|
172
189
|
bucketStorage: storage
|
|
173
190
|
});
|
|
@@ -180,11 +197,12 @@ bucket_definitions:
|
|
|
180
197
|
expect(line.checkpointLine).toEqual({
|
|
181
198
|
checkpoint: {
|
|
182
199
|
buckets: [
|
|
183
|
-
{ bucket: 'global[1]', checksum: 1, count: 1, priority: 3 },
|
|
184
|
-
{ bucket: 'global[2]', checksum: 1, count: 1, priority: 3 }
|
|
200
|
+
{ bucket: 'global[1]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] },
|
|
201
|
+
{ bucket: 'global[2]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }
|
|
185
202
|
],
|
|
186
203
|
last_op_id: '1',
|
|
187
|
-
write_checkpoint: undefined
|
|
204
|
+
write_checkpoint: undefined,
|
|
205
|
+
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
188
206
|
}
|
|
189
207
|
});
|
|
190
208
|
expect(line.bucketsToFetch).toEqual([
|
|
@@ -215,8 +233,8 @@ bucket_definitions:
|
|
|
215
233
|
checkpoint_diff: {
|
|
216
234
|
removed_buckets: [],
|
|
217
235
|
updated_buckets: [
|
|
218
|
-
{ bucket: 'global[1]', checksum: 2, count: 2, priority: 3 },
|
|
219
|
-
{ bucket: 'global[2]', checksum: 2, count: 2, priority: 3 }
|
|
236
|
+
{ bucket: 'global[1]', checksum: 2, count: 2, priority: 3, subscriptions: [{ default: 0 }] },
|
|
237
|
+
{ bucket: 'global[2]', checksum: 2, count: 2, priority: 3, subscriptions: [{ default: 0 }] }
|
|
220
238
|
],
|
|
221
239
|
last_op_id: '2',
|
|
222
240
|
write_checkpoint: undefined
|
|
@@ -232,9 +250,9 @@ bucket_definitions:
|
|
|
232
250
|
|
|
233
251
|
const state = new BucketChecksumState({
|
|
234
252
|
syncContext,
|
|
253
|
+
tokenPayload,
|
|
235
254
|
// Client sets the initial state here
|
|
236
|
-
|
|
237
|
-
syncParams: new RequestParameters({ sub: '' }, {}),
|
|
255
|
+
syncRequest: { buckets: [{ name: 'something_here[]', after: '1' }] },
|
|
238
256
|
syncRules: SYNC_RULES_GLOBAL,
|
|
239
257
|
bucketStorage: storage
|
|
240
258
|
});
|
|
@@ -249,9 +267,10 @@ bucket_definitions:
|
|
|
249
267
|
line.advance();
|
|
250
268
|
expect(line.checkpointLine).toEqual({
|
|
251
269
|
checkpoint: {
|
|
252
|
-
buckets: [{ bucket: 'global[]', checksum: 1, count: 1, priority: 3 }],
|
|
270
|
+
buckets: [{ bucket: 'global[]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }],
|
|
253
271
|
last_op_id: '1',
|
|
254
|
-
write_checkpoint: undefined
|
|
272
|
+
write_checkpoint: undefined,
|
|
273
|
+
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
255
274
|
}
|
|
256
275
|
});
|
|
257
276
|
expect(line.bucketsToFetch).toEqual([
|
|
@@ -273,7 +292,8 @@ bucket_definitions:
|
|
|
273
292
|
|
|
274
293
|
const state = new BucketChecksumState({
|
|
275
294
|
syncContext,
|
|
276
|
-
|
|
295
|
+
tokenPayload,
|
|
296
|
+
syncRequest,
|
|
277
297
|
syncRules: SYNC_RULES_GLOBAL_TWO,
|
|
278
298
|
bucketStorage: storage
|
|
279
299
|
});
|
|
@@ -310,7 +330,7 @@ bucket_definitions:
|
|
|
310
330
|
removed_buckets: [],
|
|
311
331
|
updated_buckets: [
|
|
312
332
|
// This does not include global[2], since it was not invalidated.
|
|
313
|
-
{ bucket: 'global[1]', checksum: 2, count: 2, priority: 3 }
|
|
333
|
+
{ bucket: 'global[1]', checksum: 2, count: 2, priority: 3, subscriptions: [{ default: 0 }] }
|
|
314
334
|
],
|
|
315
335
|
last_op_id: '2',
|
|
316
336
|
write_checkpoint: undefined
|
|
@@ -325,7 +345,8 @@ bucket_definitions:
|
|
|
325
345
|
|
|
326
346
|
const state = new BucketChecksumState({
|
|
327
347
|
syncContext,
|
|
328
|
-
|
|
348
|
+
tokenPayload,
|
|
349
|
+
syncRequest,
|
|
329
350
|
syncRules: SYNC_RULES_GLOBAL_TWO,
|
|
330
351
|
bucketStorage: storage
|
|
331
352
|
});
|
|
@@ -358,8 +379,8 @@ bucket_definitions:
|
|
|
358
379
|
checkpoint_diff: {
|
|
359
380
|
removed_buckets: [],
|
|
360
381
|
updated_buckets: [
|
|
361
|
-
{ bucket: 'global[1]', checksum: 2, count: 2, priority: 3 },
|
|
362
|
-
{ bucket: 'global[2]', checksum: 2, count: 2, priority: 3 }
|
|
382
|
+
{ bucket: 'global[1]', checksum: 2, count: 2, priority: 3, subscriptions: [{ default: 0 }] },
|
|
383
|
+
{ bucket: 'global[2]', checksum: 2, count: 2, priority: 3, subscriptions: [{ default: 0 }] }
|
|
363
384
|
],
|
|
364
385
|
last_op_id: '2',
|
|
365
386
|
write_checkpoint: undefined
|
|
@@ -379,7 +400,8 @@ bucket_definitions:
|
|
|
379
400
|
|
|
380
401
|
const state = new BucketChecksumState({
|
|
381
402
|
syncContext,
|
|
382
|
-
|
|
403
|
+
tokenPayload,
|
|
404
|
+
syncRequest,
|
|
383
405
|
syncRules: SYNC_RULES_GLOBAL_TWO,
|
|
384
406
|
bucketStorage: storage
|
|
385
407
|
});
|
|
@@ -393,11 +415,12 @@ bucket_definitions:
|
|
|
393
415
|
expect(line.checkpointLine).toEqual({
|
|
394
416
|
checkpoint: {
|
|
395
417
|
buckets: [
|
|
396
|
-
{ bucket: 'global[1]', checksum: 3, count: 3, priority: 3 },
|
|
397
|
-
{ bucket: 'global[2]', checksum: 3, count: 3, priority: 3 }
|
|
418
|
+
{ bucket: 'global[1]', checksum: 3, count: 3, priority: 3, subscriptions: [{ default: 0 }] },
|
|
419
|
+
{ bucket: 'global[2]', checksum: 3, count: 3, priority: 3, subscriptions: [{ default: 0 }] }
|
|
398
420
|
],
|
|
399
421
|
last_op_id: '3',
|
|
400
|
-
write_checkpoint: undefined
|
|
422
|
+
write_checkpoint: undefined,
|
|
423
|
+
streams: [{ name: 'global', is_default: true, errors: [] }]
|
|
401
424
|
}
|
|
402
425
|
});
|
|
403
426
|
expect(line.bucketsToFetch).toEqual([
|
|
@@ -444,7 +467,8 @@ bucket_definitions:
|
|
|
444
467
|
bucket: 'global[1]',
|
|
445
468
|
checksum: 4,
|
|
446
469
|
count: 4,
|
|
447
|
-
priority: 3
|
|
470
|
+
priority: 3,
|
|
471
|
+
subscriptions: [{ default: 0 }]
|
|
448
472
|
}
|
|
449
473
|
],
|
|
450
474
|
last_op_id: '4',
|
|
@@ -480,7 +504,8 @@ bucket_definitions:
|
|
|
480
504
|
|
|
481
505
|
const state = new BucketChecksumState({
|
|
482
506
|
syncContext,
|
|
483
|
-
|
|
507
|
+
tokenPayload: { sub: 'u1' },
|
|
508
|
+
syncRequest,
|
|
484
509
|
syncRules: SYNC_RULES_DYNAMIC,
|
|
485
510
|
bucketStorage: storage
|
|
486
511
|
});
|
|
@@ -496,10 +521,29 @@ bucket_definitions:
|
|
|
496
521
|
expect(line.checkpointLine).toEqual({
|
|
497
522
|
checkpoint: {
|
|
498
523
|
buckets: [
|
|
499
|
-
{
|
|
500
|
-
|
|
524
|
+
{
|
|
525
|
+
bucket: 'by_project[1]',
|
|
526
|
+
checksum: 1,
|
|
527
|
+
count: 1,
|
|
528
|
+
priority: 3,
|
|
529
|
+
subscriptions: [{ default: 0 }]
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
bucket: 'by_project[2]',
|
|
533
|
+
checksum: 1,
|
|
534
|
+
count: 1,
|
|
535
|
+
priority: 3,
|
|
536
|
+
subscriptions: [{ default: 0 }]
|
|
537
|
+
}
|
|
501
538
|
],
|
|
502
539
|
last_op_id: '1',
|
|
540
|
+
streams: [
|
|
541
|
+
{
|
|
542
|
+
is_default: true,
|
|
543
|
+
name: 'by_project',
|
|
544
|
+
errors: []
|
|
545
|
+
}
|
|
546
|
+
],
|
|
503
547
|
write_checkpoint: undefined
|
|
504
548
|
}
|
|
505
549
|
});
|
|
@@ -544,13 +588,301 @@ bucket_definitions:
|
|
|
544
588
|
expect(line2.checkpointLine).toEqual({
|
|
545
589
|
checkpoint_diff: {
|
|
546
590
|
removed_buckets: [],
|
|
547
|
-
updated_buckets: [
|
|
591
|
+
updated_buckets: [
|
|
592
|
+
{
|
|
593
|
+
bucket: 'by_project[3]',
|
|
594
|
+
checksum: 1,
|
|
595
|
+
count: 1,
|
|
596
|
+
priority: 3,
|
|
597
|
+
subscriptions: [{ default: 0 }]
|
|
598
|
+
}
|
|
599
|
+
],
|
|
548
600
|
last_op_id: '2',
|
|
549
601
|
write_checkpoint: undefined
|
|
550
602
|
}
|
|
551
603
|
});
|
|
552
604
|
expect(line2.getFilteredBucketPositions()).toEqual(new Map([['by_project[3]', 0n]]));
|
|
553
605
|
});
|
|
606
|
+
|
|
607
|
+
describe('streams', () => {
|
|
608
|
+
let source: { -readonly [P in keyof BucketSource]: BucketSource[P] };
|
|
609
|
+
let storage: MockBucketChecksumStateStorage;
|
|
610
|
+
let staticBucketIds = ['stream|0[]'];
|
|
611
|
+
|
|
612
|
+
function checksumState(options?: Partial<BucketChecksumStateOptions>) {
|
|
613
|
+
const rules = new SqlSyncRules('');
|
|
614
|
+
rules.bucketSources.push(source);
|
|
615
|
+
|
|
616
|
+
return new BucketChecksumState({
|
|
617
|
+
syncContext,
|
|
618
|
+
syncRequest,
|
|
619
|
+
tokenPayload,
|
|
620
|
+
syncRules: rules,
|
|
621
|
+
bucketStorage: storage,
|
|
622
|
+
...options
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function createQuerier(ids: string[], subscription: number | null): BucketParameterQuerier {
|
|
627
|
+
return {
|
|
628
|
+
staticBuckets: ids.map((bucket) => ({
|
|
629
|
+
definition: 'stream',
|
|
630
|
+
inclusion_reasons: subscription == null ? ['default'] : [{ subscription }],
|
|
631
|
+
bucket,
|
|
632
|
+
priority: 3
|
|
633
|
+
})),
|
|
634
|
+
hasDynamicBuckets: false,
|
|
635
|
+
parameterQueryLookups: [],
|
|
636
|
+
queryDynamicBucketDescriptions: function (): never {
|
|
637
|
+
throw new Error('no dynamic buckets.');
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
beforeEach(() => {
|
|
643
|
+
// Currently using mocked streams before streams are actually implemented as parsable rules.
|
|
644
|
+
source = {
|
|
645
|
+
name: 'stream',
|
|
646
|
+
type: BucketSourceType.SYNC_STREAM,
|
|
647
|
+
subscribedToByDefault: false,
|
|
648
|
+
pushBucketParameterQueriers(result, options) {
|
|
649
|
+
// Create a fake querier that resolves the global stream["default"] bucket by default and allows extracting
|
|
650
|
+
// additional buckets from parameters.
|
|
651
|
+
const subscriptions = options.streams['stream'] ?? [];
|
|
652
|
+
if (!this.subscribedToByDefault && !subscriptions.length) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
let hasExplicitDefaultSubscription = false;
|
|
657
|
+
for (const subscription of subscriptions) {
|
|
658
|
+
try {
|
|
659
|
+
let subscriptionParameters = [];
|
|
660
|
+
|
|
661
|
+
if (subscription.parameters != null) {
|
|
662
|
+
subscriptionParameters = JSON.parse(subscription.parameters['ids'] as string).map(
|
|
663
|
+
(e: string) => `stream["${e}"]`
|
|
664
|
+
);
|
|
665
|
+
} else {
|
|
666
|
+
hasExplicitDefaultSubscription = true;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
result.queriers.push(createQuerier([...subscriptionParameters], subscription.opaque_id));
|
|
670
|
+
} catch (e) {
|
|
671
|
+
result.errors.push({
|
|
672
|
+
descriptor: 'stream',
|
|
673
|
+
subscription,
|
|
674
|
+
message: `Error evaluating bucket ids: ${e.message}`
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// If the stream is subscribed to by default and there is no explicit subscription that would match the default
|
|
680
|
+
// subscription, also include the default querier.
|
|
681
|
+
if (this.subscribedToByDefault && !hasExplicitDefaultSubscription) {
|
|
682
|
+
result.queriers.push(createQuerier(['stream["default"]'], null));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
} satisfies Partial<BucketSource> as any;
|
|
686
|
+
|
|
687
|
+
storage = new MockBucketChecksumStateStorage();
|
|
688
|
+
storage.updateTestChecksum({ bucket: 'stream["default"]', checksum: 1, count: 1 });
|
|
689
|
+
storage.updateTestChecksum({ bucket: 'stream["a"]', checksum: 1, count: 1 });
|
|
690
|
+
storage.updateTestChecksum({ bucket: 'stream["b"]', checksum: 1, count: 1 });
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
test('includes defaults', async () => {
|
|
694
|
+
source.subscribedToByDefault = true;
|
|
695
|
+
const state = checksumState();
|
|
696
|
+
|
|
697
|
+
const line = await state.buildNextCheckpointLine({
|
|
698
|
+
base: storage.makeCheckpoint(1n),
|
|
699
|
+
writeCheckpoint: null,
|
|
700
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
701
|
+
})!;
|
|
702
|
+
line?.advance();
|
|
703
|
+
expect(line?.checkpointLine).toEqual({
|
|
704
|
+
checkpoint: {
|
|
705
|
+
buckets: [
|
|
706
|
+
{ bucket: 'stream["default"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }
|
|
707
|
+
],
|
|
708
|
+
last_op_id: '1',
|
|
709
|
+
write_checkpoint: undefined,
|
|
710
|
+
streams: [{ name: 'stream', is_default: true, errors: [] }]
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
test('can exclude defaults', async () => {
|
|
716
|
+
source.subscribedToByDefault = true;
|
|
717
|
+
const state = checksumState({ syncRequest: { streams: { include_defaults: false, subscriptions: [] } } });
|
|
718
|
+
|
|
719
|
+
const line = await state.buildNextCheckpointLine({
|
|
720
|
+
base: storage.makeCheckpoint(1n),
|
|
721
|
+
writeCheckpoint: null,
|
|
722
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
723
|
+
})!;
|
|
724
|
+
line?.advance();
|
|
725
|
+
expect(line?.checkpointLine).toEqual({
|
|
726
|
+
checkpoint: {
|
|
727
|
+
buckets: [],
|
|
728
|
+
last_op_id: '1',
|
|
729
|
+
write_checkpoint: undefined,
|
|
730
|
+
streams: []
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
test('custom subscriptions', async () => {
|
|
736
|
+
source.subscribedToByDefault = true;
|
|
737
|
+
|
|
738
|
+
const state = checksumState({
|
|
739
|
+
syncRequest: {
|
|
740
|
+
streams: {
|
|
741
|
+
subscriptions: [
|
|
742
|
+
{ stream: 'stream', parameters: { ids: '["a"]' }, override_priority: null },
|
|
743
|
+
{ stream: 'stream', parameters: { ids: '["b"]' }, override_priority: 1 }
|
|
744
|
+
]
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
const line = await state.buildNextCheckpointLine({
|
|
750
|
+
base: storage.makeCheckpoint(1n),
|
|
751
|
+
writeCheckpoint: null,
|
|
752
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
753
|
+
})!;
|
|
754
|
+
line?.advance();
|
|
755
|
+
expect(line?.checkpointLine).toEqual({
|
|
756
|
+
checkpoint: {
|
|
757
|
+
buckets: [
|
|
758
|
+
{ bucket: 'stream["a"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ sub: 0 }] },
|
|
759
|
+
{ bucket: 'stream["b"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 1 }] },
|
|
760
|
+
{ bucket: 'stream["default"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }
|
|
761
|
+
],
|
|
762
|
+
last_op_id: '1',
|
|
763
|
+
write_checkpoint: undefined,
|
|
764
|
+
streams: [{ name: 'stream', is_default: true, errors: [] }]
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
test('overlap between custom subscriptions', async () => {
|
|
770
|
+
const state = checksumState({
|
|
771
|
+
syncRequest: {
|
|
772
|
+
streams: {
|
|
773
|
+
subscriptions: [
|
|
774
|
+
{ stream: 'stream', parameters: { ids: '["a", "b"]' }, override_priority: null },
|
|
775
|
+
{ stream: 'stream', parameters: { ids: '["b"]' }, override_priority: 1 }
|
|
776
|
+
]
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
const line = await state.buildNextCheckpointLine({
|
|
782
|
+
base: storage.makeCheckpoint(1n),
|
|
783
|
+
writeCheckpoint: null,
|
|
784
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
785
|
+
})!;
|
|
786
|
+
line?.advance();
|
|
787
|
+
expect(line?.checkpointLine).toEqual({
|
|
788
|
+
checkpoint: {
|
|
789
|
+
buckets: [
|
|
790
|
+
{ bucket: 'stream["a"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ sub: 0 }] },
|
|
791
|
+
{ bucket: 'stream["b"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }, { sub: 1 }] }
|
|
792
|
+
],
|
|
793
|
+
last_op_id: '1',
|
|
794
|
+
write_checkpoint: undefined,
|
|
795
|
+
streams: [{ name: 'stream', is_default: false, errors: [] }]
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
test('overlap between default and custom subscription', async () => {
|
|
801
|
+
source.subscribedToByDefault = true;
|
|
802
|
+
const state = checksumState({
|
|
803
|
+
syncRequest: {
|
|
804
|
+
streams: {
|
|
805
|
+
subscriptions: [{ stream: 'stream', parameters: { ids: '["a", "default"]' }, override_priority: 1 }]
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
const line = await state.buildNextCheckpointLine({
|
|
811
|
+
base: storage.makeCheckpoint(1n),
|
|
812
|
+
writeCheckpoint: null,
|
|
813
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
814
|
+
})!;
|
|
815
|
+
line?.advance();
|
|
816
|
+
expect(line?.checkpointLine).toEqual({
|
|
817
|
+
checkpoint: {
|
|
818
|
+
buckets: [
|
|
819
|
+
{ bucket: 'stream["a"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }] },
|
|
820
|
+
{
|
|
821
|
+
bucket: 'stream["default"]',
|
|
822
|
+
checksum: 1,
|
|
823
|
+
count: 1,
|
|
824
|
+
priority: 1,
|
|
825
|
+
subscriptions: [{ sub: 0 }, { default: 0 }]
|
|
826
|
+
}
|
|
827
|
+
],
|
|
828
|
+
last_op_id: '1',
|
|
829
|
+
write_checkpoint: undefined,
|
|
830
|
+
streams: [{ name: 'stream', is_default: true, errors: [] }]
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test('reports errors', async () => {
|
|
836
|
+
source.subscribedToByDefault = true;
|
|
837
|
+
|
|
838
|
+
const state = checksumState({
|
|
839
|
+
syncRequest: {
|
|
840
|
+
streams: {
|
|
841
|
+
subscriptions: [
|
|
842
|
+
{ stream: 'stream', parameters: { ids: '["a", "b"]' }, override_priority: 1 },
|
|
843
|
+
{ stream: 'stream', parameters: { ids: 'invalid json' }, override_priority: null }
|
|
844
|
+
]
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
const line = await state.buildNextCheckpointLine({
|
|
850
|
+
base: storage.makeCheckpoint(1n),
|
|
851
|
+
writeCheckpoint: null,
|
|
852
|
+
update: CHECKPOINT_INVALIDATE_ALL
|
|
853
|
+
})!;
|
|
854
|
+
line?.advance();
|
|
855
|
+
expect(line?.checkpointLine).toEqual({
|
|
856
|
+
checkpoint: {
|
|
857
|
+
buckets: [
|
|
858
|
+
{ bucket: 'stream["a"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }] },
|
|
859
|
+
{ bucket: 'stream["b"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }] },
|
|
860
|
+
{
|
|
861
|
+
bucket: 'stream["default"]',
|
|
862
|
+
checksum: 1,
|
|
863
|
+
count: 1,
|
|
864
|
+
priority: 3,
|
|
865
|
+
subscriptions: [{ default: 0 }]
|
|
866
|
+
}
|
|
867
|
+
],
|
|
868
|
+
last_op_id: '1',
|
|
869
|
+
write_checkpoint: undefined,
|
|
870
|
+
streams: [
|
|
871
|
+
{
|
|
872
|
+
name: 'stream',
|
|
873
|
+
is_default: true,
|
|
874
|
+
errors: [
|
|
875
|
+
{
|
|
876
|
+
message: 'Error evaluating bucket ids: Unexpected token \'i\', "invalid json" is not valid JSON',
|
|
877
|
+
subscription: 1
|
|
878
|
+
}
|
|
879
|
+
]
|
|
880
|
+
}
|
|
881
|
+
]
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
});
|
|
554
886
|
});
|
|
555
887
|
|
|
556
888
|
class MockBucketChecksumStateStorage implements BucketChecksumStateStorage {
|