@powersync/service-core 0.0.0-dev-20250820110726 → 0.0.0-dev-20250827072023

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 (40) hide show
  1. package/CHANGELOG.md +10 -6
  2. package/dist/events/EventsEngine.d.ts +4 -1
  3. package/dist/events/EventsEngine.js +4 -3
  4. package/dist/events/EventsEngine.js.map +1 -1
  5. package/dist/routes/configure-fastify.d.ts +0 -5
  6. package/dist/routes/configure-fastify.js.map +1 -1
  7. package/dist/routes/endpoints/socket-route.js +6 -5
  8. package/dist/routes/endpoints/socket-route.js.map +1 -1
  9. package/dist/routes/endpoints/sync-stream.d.ts +0 -10
  10. package/dist/routes/endpoints/sync-stream.js +8 -5
  11. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  12. package/dist/routes/router.d.ts +3 -3
  13. package/dist/storage/BucketStorageBatch.d.ts +4 -4
  14. package/dist/storage/BucketStorageBatch.js.map +1 -1
  15. package/dist/storage/BucketStorageFactory.d.ts +2 -0
  16. package/dist/storage/ReplicationEventPayload.d.ts +2 -2
  17. package/dist/storage/ReportStorage.d.ts +30 -2
  18. package/dist/sync/sync.d.ts +1 -0
  19. package/dist/sync/sync.js +18 -35
  20. package/dist/sync/sync.js.map +1 -1
  21. package/dist/util/protocol-types.d.ts +0 -4
  22. package/dist/util/protocol-types.js +0 -4
  23. package/dist/util/protocol-types.js.map +1 -1
  24. package/dist/util/utils.d.ts +1 -1
  25. package/dist/util/utils.js.map +1 -1
  26. package/package.json +6 -6
  27. package/src/events/EventsEngine.ts +4 -4
  28. package/src/routes/configure-fastify.ts +0 -1
  29. package/src/routes/endpoints/socket-route.ts +6 -5
  30. package/src/routes/endpoints/sync-stream.ts +8 -6
  31. package/src/routes/router.ts +3 -3
  32. package/src/storage/BucketStorageBatch.ts +10 -4
  33. package/src/storage/BucketStorageFactory.ts +2 -0
  34. package/src/storage/ReplicationEventPayload.ts +2 -2
  35. package/src/storage/ReportStorage.ts +30 -2
  36. package/src/sync/sync.ts +36 -36
  37. package/src/util/protocol-types.ts +0 -5
  38. package/src/util/utils.ts +4 -1
  39. package/test/src/sync/BucketChecksumState.test.ts +36 -92
  40. package/tsconfig.tsbuildinfo +1 -1
@@ -607,11 +607,23 @@ bucket_definitions:
607
607
  describe('streams', () => {
608
608
  let source: { -readonly [P in keyof BucketSource]: BucketSource[P] };
609
609
  let storage: MockBucketChecksumStateStorage;
610
- let staticBucketIds = ['stream|0[]'];
611
610
 
612
- function checksumState(options?: Partial<BucketChecksumStateOptions>) {
613
- const rules = new SqlSyncRules('');
614
- rules.bucketSources.push(source);
611
+ function checksumState(source: string | boolean, options?: Partial<BucketChecksumStateOptions>) {
612
+ if (typeof source == 'boolean') {
613
+ source = `
614
+ streams:
615
+ stream:
616
+ auto_subscribe: ${source}
617
+ query: SELECT * FROM assets WHERE id IN ifnull(subscription.parameter('ids'), '["default"]');
618
+
619
+ config:
620
+ edition: 2
621
+ `;
622
+ }
623
+
624
+ const rules = SqlSyncRules.fromYaml(source, {
625
+ defaultSchema: 'public'
626
+ });
615
627
 
616
628
  return new BucketChecksumState({
617
629
  syncContext,
@@ -623,77 +635,15 @@ bucket_definitions:
623
635
  });
624
636
  }
625
637
 
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
638
  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
639
  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 });
640
+ storage.updateTestChecksum({ bucket: 'stream|0["default"]', checksum: 1, count: 1 });
641
+ storage.updateTestChecksum({ bucket: 'stream|0["a"]', checksum: 1, count: 1 });
642
+ storage.updateTestChecksum({ bucket: 'stream|0["b"]', checksum: 1, count: 1 });
691
643
  });
692
644
 
693
645
  test('includes defaults', async () => {
694
- source.subscribedToByDefault = true;
695
- const state = checksumState();
696
-
646
+ const state = checksumState(true);
697
647
  const line = await state.buildNextCheckpointLine({
698
648
  base: storage.makeCheckpoint(1n),
699
649
  writeCheckpoint: null,
@@ -703,7 +653,7 @@ bucket_definitions:
703
653
  expect(line?.checkpointLine).toEqual({
704
654
  checkpoint: {
705
655
  buckets: [
706
- { bucket: 'stream["default"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }
656
+ { bucket: 'stream|0["default"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }
707
657
  ],
708
658
  last_op_id: '1',
709
659
  write_checkpoint: undefined,
@@ -713,8 +663,7 @@ bucket_definitions:
713
663
  });
714
664
 
715
665
  test('can exclude defaults', async () => {
716
- source.subscribedToByDefault = true;
717
- const state = checksumState({ syncRequest: { streams: { include_defaults: false, subscriptions: [] } } });
666
+ const state = checksumState(true, { syncRequest: { streams: { include_defaults: false, subscriptions: [] } } });
718
667
 
719
668
  const line = await state.buildNextCheckpointLine({
720
669
  base: storage.makeCheckpoint(1n),
@@ -733,9 +682,7 @@ bucket_definitions:
733
682
  });
734
683
 
735
684
  test('custom subscriptions', async () => {
736
- source.subscribedToByDefault = true;
737
-
738
- const state = checksumState({
685
+ const state = checksumState(true, {
739
686
  syncRequest: {
740
687
  streams: {
741
688
  subscriptions: [
@@ -755,9 +702,9 @@ bucket_definitions:
755
702
  expect(line?.checkpointLine).toEqual({
756
703
  checkpoint: {
757
704
  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 }] }
705
+ { bucket: 'stream|0["a"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ sub: 0 }] },
706
+ { bucket: 'stream|0["b"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 1 }] },
707
+ { bucket: 'stream|0["default"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ default: 0 }] }
761
708
  ],
762
709
  last_op_id: '1',
763
710
  write_checkpoint: undefined,
@@ -767,7 +714,7 @@ bucket_definitions:
767
714
  });
768
715
 
769
716
  test('overlap between custom subscriptions', async () => {
770
- const state = checksumState({
717
+ const state = checksumState(false, {
771
718
  syncRequest: {
772
719
  streams: {
773
720
  subscriptions: [
@@ -787,8 +734,8 @@ bucket_definitions:
787
734
  expect(line?.checkpointLine).toEqual({
788
735
  checkpoint: {
789
736
  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 }] }
737
+ { bucket: 'stream|0["a"]', checksum: 1, count: 1, priority: 3, subscriptions: [{ sub: 0 }] },
738
+ { bucket: 'stream|0["b"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }, { sub: 1 }] }
792
739
  ],
793
740
  last_op_id: '1',
794
741
  write_checkpoint: undefined,
@@ -798,8 +745,7 @@ bucket_definitions:
798
745
  });
799
746
 
800
747
  test('overlap between default and custom subscription', async () => {
801
- source.subscribedToByDefault = true;
802
- const state = checksumState({
748
+ const state = checksumState(true, {
803
749
  syncRequest: {
804
750
  streams: {
805
751
  subscriptions: [{ stream: 'stream', parameters: { ids: '["a", "default"]' }, override_priority: 1 }]
@@ -816,9 +762,9 @@ bucket_definitions:
816
762
  expect(line?.checkpointLine).toEqual({
817
763
  checkpoint: {
818
764
  buckets: [
819
- { bucket: 'stream["a"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }] },
765
+ { bucket: 'stream|0["a"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }] },
820
766
  {
821
- bucket: 'stream["default"]',
767
+ bucket: 'stream|0["default"]',
822
768
  checksum: 1,
823
769
  count: 1,
824
770
  priority: 1,
@@ -833,9 +779,7 @@ bucket_definitions:
833
779
  });
834
780
 
835
781
  test('reports errors', async () => {
836
- source.subscribedToByDefault = true;
837
-
838
- const state = checksumState({
782
+ const state = checksumState(true, {
839
783
  syncRequest: {
840
784
  streams: {
841
785
  subscriptions: [
@@ -855,10 +799,10 @@ bucket_definitions:
855
799
  expect(line?.checkpointLine).toEqual({
856
800
  checkpoint: {
857
801
  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 }] },
802
+ { bucket: 'stream|0["a"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }] },
803
+ { bucket: 'stream|0["b"]', checksum: 1, count: 1, priority: 1, subscriptions: [{ sub: 0 }] },
860
804
  {
861
- bucket: 'stream["default"]',
805
+ bucket: 'stream|0["default"]',
862
806
  checksum: 1,
863
807
  count: 1,
864
808
  priority: 3,