@powersync/common 1.50.0 → 1.51.0
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/dist/bundle.cjs +128 -37
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +128 -38
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +128 -37
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +128 -38
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +33 -4
- package/lib/utils/mutex.d.ts +32 -3
- package/lib/utils/mutex.js +85 -36
- package/lib/utils/mutex.js.map +1 -1
- package/lib/utils/queue.d.ts +16 -0
- package/lib/utils/queue.js +42 -0
- package/lib/utils/queue.js.map +1 -0
- package/package.json +1 -1
- package/src/utils/mutex.ts +111 -48
- package/src/utils/queue.ts +48 -0
package/dist/bundle.cjs
CHANGED
|
@@ -782,19 +782,69 @@ class SyncingService {
|
|
|
782
782
|
}
|
|
783
783
|
|
|
784
784
|
/**
|
|
785
|
-
*
|
|
785
|
+
* A simple fixed-capacity queue implementation.
|
|
786
|
+
*
|
|
787
|
+
* Unlike a naive queue implemented by `array.push()` and `array.shift()`, this avoids moving array elements around
|
|
788
|
+
* and is `O(1)` for {@link addLast} and {@link removeFirst}.
|
|
789
|
+
*/
|
|
790
|
+
class Queue {
|
|
791
|
+
table;
|
|
792
|
+
// Index of the first element in the table.
|
|
793
|
+
head;
|
|
794
|
+
// Amount of items currently in the queue.
|
|
795
|
+
_length;
|
|
796
|
+
constructor(initialItems) {
|
|
797
|
+
this.table = [...initialItems];
|
|
798
|
+
this.head = 0;
|
|
799
|
+
this._length = this.table.length;
|
|
800
|
+
}
|
|
801
|
+
get isEmpty() {
|
|
802
|
+
return this.length == 0;
|
|
803
|
+
}
|
|
804
|
+
get length() {
|
|
805
|
+
return this._length;
|
|
806
|
+
}
|
|
807
|
+
removeFirst() {
|
|
808
|
+
if (this.isEmpty) {
|
|
809
|
+
throw new Error('Queue is empty');
|
|
810
|
+
}
|
|
811
|
+
const result = this.table[this.head];
|
|
812
|
+
this._length--;
|
|
813
|
+
this.table[this.head] = undefined;
|
|
814
|
+
this.head = (this.head + 1) % this.table.length;
|
|
815
|
+
return result;
|
|
816
|
+
}
|
|
817
|
+
addLast(element) {
|
|
818
|
+
if (this.length == this.table.length) {
|
|
819
|
+
throw new Error('Queue is full');
|
|
820
|
+
}
|
|
821
|
+
this.table[(this.head + this._length) % this.table.length] = element;
|
|
822
|
+
this._length++;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* An asynchronous semaphore implementation with associated items per lease.
|
|
786
828
|
*
|
|
787
829
|
* @internal This class is meant to be used in PowerSync SDKs only, and is not part of the public API.
|
|
788
830
|
*/
|
|
789
|
-
class
|
|
790
|
-
|
|
831
|
+
class Semaphore {
|
|
832
|
+
// Available items that are not currently assigned to a waiter.
|
|
833
|
+
available;
|
|
834
|
+
size;
|
|
791
835
|
// Linked list of waiters. We don't expect the wait list to become particularly large, and this allows removing
|
|
792
836
|
// aborted waiters from the middle of the list efficiently.
|
|
793
837
|
firstWaiter;
|
|
794
838
|
lastWaiter;
|
|
795
|
-
|
|
839
|
+
constructor(elements) {
|
|
840
|
+
this.available = new Queue(elements);
|
|
841
|
+
this.size = this.available.length;
|
|
842
|
+
}
|
|
843
|
+
addWaiter(requestedItems, onAcquire) {
|
|
796
844
|
const node = {
|
|
797
845
|
isActive: true,
|
|
846
|
+
acquiredItems: [],
|
|
847
|
+
remainingItems: requestedItems,
|
|
798
848
|
onAcquire,
|
|
799
849
|
prev: this.lastWaiter
|
|
800
850
|
};
|
|
@@ -820,52 +870,92 @@ class Mutex {
|
|
|
820
870
|
if (waiter == this.lastWaiter)
|
|
821
871
|
this.lastWaiter = prev;
|
|
822
872
|
}
|
|
823
|
-
|
|
873
|
+
requestPermits(amount, abort) {
|
|
874
|
+
if (amount <= 0 || amount > this.size) {
|
|
875
|
+
throw new Error(`Invalid amount of items requested (${amount}), must be between 1 and ${this.size}`);
|
|
876
|
+
}
|
|
824
877
|
return new Promise((resolve, reject) => {
|
|
825
878
|
function rejectAborted() {
|
|
826
|
-
reject(abort?.reason ?? new Error('
|
|
879
|
+
reject(abort?.reason ?? new Error('Semaphore acquire aborted'));
|
|
827
880
|
}
|
|
828
881
|
if (abort?.aborted) {
|
|
829
882
|
return rejectAborted();
|
|
830
883
|
}
|
|
831
|
-
let
|
|
884
|
+
let waiter;
|
|
832
885
|
const markCompleted = () => {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
886
|
+
const items = waiter.acquiredItems;
|
|
887
|
+
waiter.acquiredItems = []; // Avoid releasing items twice.
|
|
888
|
+
for (const element of items) {
|
|
889
|
+
// Give to next waiter, if possible.
|
|
890
|
+
const nextWaiter = this.firstWaiter;
|
|
891
|
+
if (nextWaiter) {
|
|
892
|
+
nextWaiter.acquiredItems.push(element);
|
|
893
|
+
nextWaiter.remainingItems--;
|
|
894
|
+
if (nextWaiter.remainingItems == 0) {
|
|
895
|
+
nextWaiter.onAcquire();
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
// No pending waiter, return lease into pool.
|
|
900
|
+
this.available.addLast(element);
|
|
901
|
+
}
|
|
841
902
|
}
|
|
842
|
-
|
|
843
|
-
|
|
903
|
+
};
|
|
904
|
+
const onAbort = () => {
|
|
905
|
+
abort?.removeEventListener('abort', onAbort);
|
|
906
|
+
if (waiter.isActive) {
|
|
907
|
+
this.deactivateWaiter(waiter);
|
|
908
|
+
rejectAborted();
|
|
844
909
|
}
|
|
845
910
|
};
|
|
846
|
-
|
|
847
|
-
this.
|
|
848
|
-
|
|
849
|
-
|
|
911
|
+
const resolvePromise = () => {
|
|
912
|
+
this.deactivateWaiter(waiter);
|
|
913
|
+
abort?.removeEventListener('abort', onAbort);
|
|
914
|
+
const items = waiter.acquiredItems;
|
|
915
|
+
resolve({ items, release: markCompleted });
|
|
916
|
+
};
|
|
917
|
+
waiter = this.addWaiter(amount, resolvePromise);
|
|
918
|
+
// If there are items in the pool that haven't been assigned, we can pull them into this waiter. Note that this is
|
|
919
|
+
// only the case if we're the first waiter (otherwise, items would have been assigned to an earlier waiter).
|
|
920
|
+
while (!this.available.isEmpty && waiter.remainingItems > 0) {
|
|
921
|
+
waiter.acquiredItems.push(this.available.removeFirst());
|
|
922
|
+
waiter.remainingItems--;
|
|
850
923
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
const onAbort = () => {
|
|
854
|
-
abort?.removeEventListener('abort', onAbort);
|
|
855
|
-
if (node.isActive) {
|
|
856
|
-
this.deactivateWaiter(node);
|
|
857
|
-
rejectAborted();
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
node = this.addWaiter(() => {
|
|
861
|
-
abort?.removeEventListener('abort', onAbort);
|
|
862
|
-
holdsMutex = true;
|
|
863
|
-
resolve(markCompleted);
|
|
864
|
-
});
|
|
865
|
-
abort?.addEventListener('abort', onAbort);
|
|
924
|
+
if (waiter.remainingItems == 0) {
|
|
925
|
+
return resolvePromise();
|
|
866
926
|
}
|
|
927
|
+
abort?.addEventListener('abort', onAbort);
|
|
867
928
|
});
|
|
868
929
|
}
|
|
930
|
+
/**
|
|
931
|
+
* Requests a single item from the pool.
|
|
932
|
+
*
|
|
933
|
+
* The returned `release` callback must be invoked to return the item into the pool.
|
|
934
|
+
*/
|
|
935
|
+
async requestOne(abort) {
|
|
936
|
+
const { items, release } = await this.requestPermits(1, abort);
|
|
937
|
+
return { release, item: items[0] };
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Requests access to all items from the pool.
|
|
941
|
+
*
|
|
942
|
+
* The returned `release` callback must be invoked to return items into the pool.
|
|
943
|
+
*/
|
|
944
|
+
requestAll(abort) {
|
|
945
|
+
return this.requestPermits(this.size, abort);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* An asynchronous mutex implementation.
|
|
950
|
+
*
|
|
951
|
+
* @internal This class is meant to be used in PowerSync SDKs only, and is not part of the public API.
|
|
952
|
+
*/
|
|
953
|
+
class Mutex {
|
|
954
|
+
inner = new Semaphore([null]);
|
|
955
|
+
async acquire(abort) {
|
|
956
|
+
const { release } = await this.inner.requestOne(abort);
|
|
957
|
+
return release;
|
|
958
|
+
}
|
|
869
959
|
async runExclusive(fn, abort) {
|
|
870
960
|
const returnMutex = await this.acquire(abort);
|
|
871
961
|
try {
|
|
@@ -10662,7 +10752,7 @@ function requireDist () {
|
|
|
10662
10752
|
|
|
10663
10753
|
var distExports = requireDist();
|
|
10664
10754
|
|
|
10665
|
-
var version = "1.
|
|
10755
|
+
var version = "1.51.0";
|
|
10666
10756
|
var PACKAGE = {
|
|
10667
10757
|
version: version};
|
|
10668
10758
|
|
|
@@ -14654,6 +14744,7 @@ exports.OnChangeQueryProcessor = OnChangeQueryProcessor;
|
|
|
14654
14744
|
exports.OpType = OpType;
|
|
14655
14745
|
exports.OplogEntry = OplogEntry;
|
|
14656
14746
|
exports.Schema = Schema;
|
|
14747
|
+
exports.Semaphore = Semaphore;
|
|
14657
14748
|
exports.SqliteBucketStorage = SqliteBucketStorage;
|
|
14658
14749
|
exports.SyncDataBatch = SyncDataBatch;
|
|
14659
14750
|
exports.SyncDataBucket = SyncDataBucket;
|