@keepkit/core 0.28.4 → 0.28.5
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/README.md +24 -4
- package/dist/{chunk-Y4U5DEGG.js → chunk-AUNFZU3A.js} +61 -6
- package/dist/chunk-AUNFZU3A.js.map +1 -0
- package/dist/{chunk-2TENUU7B.js → chunk-NZLAW4LY.js} +294 -30
- package/dist/chunk-NZLAW4LY.js.map +1 -0
- package/dist/core.d.ts +164 -7
- package/dist/core.js +469 -7
- package/dist/core.js.map +1 -1
- package/dist/react.d.ts +5 -5
- package/dist/react.js +2 -2
- package/dist/schema.d.ts +1 -1
- package/dist/{scope-Dtkxpnof.d.ts → scope-BteyH-b9.d.ts} +6 -3
- package/dist/storage.d.ts +28 -9
- package/dist/storage.js +1 -1
- package/dist/{store-45yaDeER.d.ts → store-DVP3QyVp.d.ts} +13 -4
- package/dist/{types-C_U5Q44o.d.ts → types-SKIL2p2c.d.ts} +34 -3
- package/package.json +1 -1
- package/dist/chunk-2TENUU7B.js.map +0 -1
- package/dist/chunk-Y4U5DEGG.js.map +0 -1
|
@@ -84,6 +84,9 @@ function isKeepItemArray(value) {
|
|
|
84
84
|
function isKeepCollectionDefinition(value) {
|
|
85
85
|
return isRecord(value) && typeof value.id === "string" && value.id.trim().length > 0 && typeof value.name === "string" && value.name.trim().length > 0 && (value.scope === void 0 || isSyncScope(value.scope));
|
|
86
86
|
}
|
|
87
|
+
function isKeepCollectionMembership(value) {
|
|
88
|
+
return isRecord(value) && typeof value.collectionId === "string" && value.collectionId.trim().length > 0 && typeof value.itemId === "string" && value.itemId.trim().length > 0 && typeof value.order === "number" && Number.isInteger(value.order) && Number.isFinite(value.order) && value.order >= 0 && (value.scope === void 0 || isSyncScope(value.scope));
|
|
89
|
+
}
|
|
87
90
|
function isRecord(value) {
|
|
88
91
|
return typeof value === "object" && value !== null;
|
|
89
92
|
}
|
|
@@ -116,6 +119,17 @@ var ScopedStorageAdapter = class {
|
|
|
116
119
|
this.setCollection = (collection) => setCollection({ ...collection, ...this.scope ? { scope: this.scope } : {} });
|
|
117
120
|
this.removeCollection = (id) => removeCollection(id, this.scope);
|
|
118
121
|
}
|
|
122
|
+
if (base.getCollectionMemberships && base.setCollectionMembership && base.removeCollectionMembership) {
|
|
123
|
+
const getMemberships = base.getCollectionMemberships.bind(base);
|
|
124
|
+
const setMembership = base.setCollectionMembership.bind(base);
|
|
125
|
+
const removeMembership = base.removeCollectionMembership.bind(base);
|
|
126
|
+
this.getCollectionMemberships = async () => {
|
|
127
|
+
const memberships = await getMemberships();
|
|
128
|
+
return this.scope ? memberships.filter((membership) => isSameKeepScope(membership.scope, this.scope)) : memberships;
|
|
129
|
+
};
|
|
130
|
+
this.setCollectionMembership = (membership) => setMembership({ ...membership, ...this.scope ? { scope: this.scope } : {} });
|
|
131
|
+
this.removeCollectionMembership = (collectionId, itemId) => removeMembership(collectionId, itemId, this.scope);
|
|
132
|
+
}
|
|
119
133
|
}
|
|
120
134
|
async getAll() {
|
|
121
135
|
const items = await this.base.getAll();
|
|
@@ -225,7 +239,7 @@ var LocalStorageSyncQueueAdapter = class {
|
|
|
225
239
|
} catch (cause) {
|
|
226
240
|
throw Object.assign(new Error("KeepKit sync queue contains invalid JSON."), { cause });
|
|
227
241
|
}
|
|
228
|
-
if (!Array.isArray(value) || !value.every(
|
|
242
|
+
if (!Array.isArray(value) || !value.every(isSyncQueueOperation)) {
|
|
229
243
|
throw new Error("KeepKit sync queue contains invalid operations.");
|
|
230
244
|
}
|
|
231
245
|
return value;
|
|
@@ -255,7 +269,7 @@ var IndexedDBSyncQueueAdapter = class {
|
|
|
255
269
|
if (!database) return [];
|
|
256
270
|
const transaction = database.transaction(this.storeName, "readonly");
|
|
257
271
|
const value = await requestToPromise(transaction.objectStore(this.storeName).getAll());
|
|
258
|
-
if (!Array.isArray(value) || !value.every(
|
|
272
|
+
if (!Array.isArray(value) || !value.every(isSyncQueueOperation)) {
|
|
259
273
|
throw new Error("KeepKit sync queue contains invalid operations.");
|
|
260
274
|
}
|
|
261
275
|
return value;
|
|
@@ -385,8 +399,59 @@ var SyncStorageAdapter = class {
|
|
|
385
399
|
const scope = this.scope;
|
|
386
400
|
return scope ? collections.filter((collection) => sameScope(collection.scope, scope)) : collections;
|
|
387
401
|
};
|
|
388
|
-
this.setCollection = (collection) =>
|
|
389
|
-
|
|
402
|
+
this.setCollection = async (collection) => {
|
|
403
|
+
const scopedCollection = { ...collection, ...this.scope ? { scope: this.scope } : {} };
|
|
404
|
+
if (this.remote.pushCollection) {
|
|
405
|
+
const operation = this.createCollectionOperation("upsert", scopedCollection.id, scopedCollection);
|
|
406
|
+
await this.enqueueBeforeLocalWrite(operation);
|
|
407
|
+
try {
|
|
408
|
+
await setCollection(scopedCollection);
|
|
409
|
+
} catch (cause) {
|
|
410
|
+
await this.removeQueued(operation.operationId);
|
|
411
|
+
throw cause;
|
|
412
|
+
}
|
|
413
|
+
this.notifyDataListeners();
|
|
414
|
+
this.setPendingState();
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
await setCollection(scopedCollection);
|
|
418
|
+
this.notifyDataListeners();
|
|
419
|
+
};
|
|
420
|
+
this.removeCollection = async (id) => {
|
|
421
|
+
if (this.remote.pushCollection) {
|
|
422
|
+
const operation = this.createCollectionOperation("remove", id);
|
|
423
|
+
await this.enqueueBeforeLocalWrite(operation);
|
|
424
|
+
try {
|
|
425
|
+
await removeCollection(id, this.scope);
|
|
426
|
+
} catch (cause) {
|
|
427
|
+
await this.removeQueued(operation.operationId);
|
|
428
|
+
throw cause;
|
|
429
|
+
}
|
|
430
|
+
this.notifyDataListeners();
|
|
431
|
+
this.setPendingState();
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
await removeCollection(id, this.scope);
|
|
435
|
+
this.notifyDataListeners();
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (this.local.getCollectionMemberships && this.local.setCollectionMembership && this.local.removeCollectionMembership) {
|
|
439
|
+
const getMemberships = this.local.getCollectionMemberships.bind(this.local);
|
|
440
|
+
const setMembership = this.local.setCollectionMembership.bind(this.local);
|
|
441
|
+
const removeMembership = this.local.removeCollectionMembership.bind(this.local);
|
|
442
|
+
this.getCollectionMemberships = async () => {
|
|
443
|
+
const memberships = await getMemberships();
|
|
444
|
+
const scope = this.scope;
|
|
445
|
+
return scope ? memberships.filter((membership) => sameScope(membership.scope, scope)) : memberships;
|
|
446
|
+
};
|
|
447
|
+
this.setCollectionMembership = async (membership) => {
|
|
448
|
+
await setMembership({ ...membership, ...this.scope ? { scope: this.scope } : {} });
|
|
449
|
+
this.notifyDataListeners();
|
|
450
|
+
};
|
|
451
|
+
this.removeCollectionMembership = async (collectionId, itemId) => {
|
|
452
|
+
await removeMembership(collectionId, itemId, this.scope);
|
|
453
|
+
this.notifyDataListeners();
|
|
454
|
+
};
|
|
390
455
|
}
|
|
391
456
|
if (typeof window !== "undefined") {
|
|
392
457
|
this.onlineHandler = () => void this.flushSync();
|
|
@@ -522,6 +587,16 @@ var SyncStorageAdapter = class {
|
|
|
522
587
|
for (const operation of [...this.queueItems]) {
|
|
523
588
|
if (this.disposed) return;
|
|
524
589
|
try {
|
|
590
|
+
if (isCollectionSyncOperation(operation)) {
|
|
591
|
+
if (!this.remote.pushCollection) throw new Error("Remote sync driver does not support collection sync.");
|
|
592
|
+
await this.pushCollectionWithRetry(operation);
|
|
593
|
+
await this.removeQueued(operation.operationId);
|
|
594
|
+
this.updateState({
|
|
595
|
+
status: this.queueItems.length > 0 ? "syncing" : "synced",
|
|
596
|
+
lastSyncedAt: this.now()
|
|
597
|
+
});
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
525
600
|
const result = await this.pushWithRetry(operation);
|
|
526
601
|
if (result.type === "conflict") {
|
|
527
602
|
const local = operation.item;
|
|
@@ -586,6 +661,17 @@ var SyncStorageAdapter = class {
|
|
|
586
661
|
...this.scope ? { scope: this.scope } : {}
|
|
587
662
|
};
|
|
588
663
|
}
|
|
664
|
+
createCollectionOperation(type, id, collection) {
|
|
665
|
+
return {
|
|
666
|
+
operationId: `${this.clientId}:${this.now()}:${createId()}`,
|
|
667
|
+
type,
|
|
668
|
+
entity: "collection",
|
|
669
|
+
id,
|
|
670
|
+
...collection ? { collection } : {},
|
|
671
|
+
createdAt: this.now(),
|
|
672
|
+
...this.scope ? { scope: this.scope } : {}
|
|
673
|
+
};
|
|
674
|
+
}
|
|
589
675
|
applyScope(item) {
|
|
590
676
|
return this.scope ? { ...item, scope: this.scope } : item;
|
|
591
677
|
}
|
|
@@ -602,6 +688,20 @@ var SyncStorageAdapter = class {
|
|
|
602
688
|
}
|
|
603
689
|
}
|
|
604
690
|
}
|
|
691
|
+
async pushCollectionWithRetry(operation) {
|
|
692
|
+
let attempt = 0;
|
|
693
|
+
while (true) {
|
|
694
|
+
try {
|
|
695
|
+
await this.remote.pushCollection?.({ ...operation, attempts: attempt });
|
|
696
|
+
return;
|
|
697
|
+
} catch (error) {
|
|
698
|
+
if (isKeepSyncAuthError(error) || attempt >= this.maxRetries) throw error;
|
|
699
|
+
attempt += 1;
|
|
700
|
+
const delay = this.retryDelayMs * this.retryBackoff ** (attempt - 1);
|
|
701
|
+
if (delay > 0) await wait(delay);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
605
705
|
async enqueueBeforeLocalWrite(operation) {
|
|
606
706
|
await this.enqueueManyBeforeLocalWrite([operation]);
|
|
607
707
|
}
|
|
@@ -610,7 +710,8 @@ var SyncStorageAdapter = class {
|
|
|
610
710
|
const next = [...this.queueItems];
|
|
611
711
|
for (const operation of operations) {
|
|
612
712
|
for (let index = next.length - 1; index >= 0; index -= 1) {
|
|
613
|
-
|
|
713
|
+
const queued = next[index];
|
|
714
|
+
if (!queued || getQueueIdentity(queued) !== getQueueIdentity(operation)) continue;
|
|
614
715
|
next.splice(index, 1);
|
|
615
716
|
}
|
|
616
717
|
next.push(operation);
|
|
@@ -670,30 +771,53 @@ var SyncStorageAdapter = class {
|
|
|
670
771
|
});
|
|
671
772
|
}
|
|
672
773
|
async pullRemote() {
|
|
673
|
-
if (!this.remote.pull) return true;
|
|
774
|
+
if (!this.remote.pull && !this.remote.pullCollections) return true;
|
|
674
775
|
try {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
const
|
|
684
|
-
|
|
776
|
+
if (this.remote.pull) {
|
|
777
|
+
const remoteItems = await this.remote.pull();
|
|
778
|
+
const pendingIds = new Set(
|
|
779
|
+
this.queueItems.filter((operation) => operation.entity !== "collection").map((operation) => operation.id)
|
|
780
|
+
);
|
|
781
|
+
const localItems = await this.getAll();
|
|
782
|
+
const localById = new Map(localItems.map((item) => [item.id, item]));
|
|
783
|
+
const remoteById = /* @__PURE__ */ new Map();
|
|
784
|
+
for (const item of remoteItems) {
|
|
785
|
+
if (this.scope && item.scope && !sameScope(item.scope, this.scope)) continue;
|
|
786
|
+
if (pendingIds.has(item.id)) continue;
|
|
787
|
+
const previous = remoteById.get(item.id);
|
|
788
|
+
remoteById.set(item.id, previous ? mergeKeepItemLists([item], [previous])[0] ?? item : item);
|
|
789
|
+
}
|
|
790
|
+
const incoming = [...remoteById.values()].flatMap((item) => {
|
|
791
|
+
const current = localById.get(item.id);
|
|
792
|
+
if (current && item.updatedAt < current.updatedAt && (item.lastOpenedAt ?? 0) <= (current.lastOpenedAt ?? 0)) {
|
|
793
|
+
return [];
|
|
794
|
+
}
|
|
795
|
+
const merged = current ? mergeKeepItemLists([item], [current])[0] ?? item : item;
|
|
796
|
+
return [this.applyScope(merged)];
|
|
797
|
+
});
|
|
798
|
+
if (incoming.length > 0) {
|
|
799
|
+
await persistKeepItems(this.local, incoming);
|
|
800
|
+
this.notifyDataListeners();
|
|
801
|
+
}
|
|
685
802
|
}
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
803
|
+
if (this.remote.pullCollections && this.local.getCollections && this.local.setCollection && this.local.removeCollection) {
|
|
804
|
+
const remoteCollections = await this.remote.pullCollections();
|
|
805
|
+
const localCollections = await this.local.getCollections();
|
|
806
|
+
const pendingIds = new Set(this.queueItems.filter(isCollectionSyncOperation).map((operation) => operation.id));
|
|
807
|
+
const remoteById = new Map(
|
|
808
|
+
remoteCollections.filter((collection) => !this.scope || sameScope(collection.scope, this.scope)).map((collection) => [collection.id, collection])
|
|
809
|
+
);
|
|
810
|
+
for (const collection of remoteById.values()) {
|
|
811
|
+
if (pendingIds.has(collection.id)) continue;
|
|
812
|
+
await this.local.setCollection({ ...collection, ...this.scope ? { scope: this.scope } : {} });
|
|
690
813
|
}
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
814
|
+
for (const collection of localCollections) {
|
|
815
|
+
if (pendingIds.has(collection.id) || remoteById.has(collection.id)) continue;
|
|
816
|
+
if (this.scope && !sameScope(collection.scope, this.scope)) continue;
|
|
817
|
+
await this.local.removeCollection(collection.id, this.scope);
|
|
818
|
+
}
|
|
819
|
+
this.notifyDataListeners();
|
|
820
|
+
}
|
|
697
821
|
return true;
|
|
698
822
|
} catch (error) {
|
|
699
823
|
this.updateState({ status: "error", error });
|
|
@@ -714,7 +838,17 @@ var SyncStorageAdapter = class {
|
|
|
714
838
|
};
|
|
715
839
|
function isSyncOperation(value) {
|
|
716
840
|
if (!isRecord(value)) return false;
|
|
717
|
-
return typeof value.operationId === "string" && (value.type === "upsert" || value.type === "remove") && typeof value.id === "string" && typeof value.createdAt === "number";
|
|
841
|
+
return typeof value.operationId === "string" && (value.type === "upsert" || value.type === "remove") && typeof value.id === "string" && typeof value.createdAt === "number" && (value.entity === void 0 || value.entity === "item" || value.entity === "collection") && (value.collection === void 0 || isKeepCollectionDefinition(value.collection)) && (value.entity !== "collection" || value.type !== "upsert" || isKeepCollectionDefinition(value.collection));
|
|
842
|
+
}
|
|
843
|
+
function isCollectionSyncOperation(value) {
|
|
844
|
+
if (!isRecord(value)) return false;
|
|
845
|
+
return typeof value.operationId === "string" && value.entity === "collection" && (value.type === "upsert" || value.type === "remove") && typeof value.id === "string" && typeof value.createdAt === "number" && (value.collection === void 0 || isKeepCollectionDefinition(value.collection));
|
|
846
|
+
}
|
|
847
|
+
function isSyncQueueOperation(value) {
|
|
848
|
+
return isSyncOperation(value) || isCollectionSyncOperation(value);
|
|
849
|
+
}
|
|
850
|
+
function getQueueIdentity(operation) {
|
|
851
|
+
return `${operation.entity === "collection" ? "collection" : "item"}:${operation.id}`;
|
|
718
852
|
}
|
|
719
853
|
function createId() {
|
|
720
854
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
@@ -820,6 +954,22 @@ var FallbackStorageAdapter = class {
|
|
|
820
954
|
return adapter.removeCollection(id, scope);
|
|
821
955
|
});
|
|
822
956
|
}
|
|
957
|
+
if (this.primary.getCollectionMemberships && this.primary.setCollectionMembership && this.primary.removeCollectionMembership && this.fallback.getCollectionMemberships && this.fallback.setCollectionMembership && this.fallback.removeCollectionMembership) {
|
|
958
|
+
const fallbackGetMemberships = this.fallback.getCollectionMemberships.bind(this.fallback);
|
|
959
|
+
this.getCollectionMemberships = async () => {
|
|
960
|
+
const memberships = await this.execute(
|
|
961
|
+
(adapter) => adapter.getCollectionMemberships?.() ?? Promise.resolve([])
|
|
962
|
+
);
|
|
963
|
+
if (this.active !== "primary" || !this.migrateFallbackOnEmpty || memberships.length > 0) return memberships;
|
|
964
|
+
const fallbackMemberships = await fallbackGetMemberships();
|
|
965
|
+
for (const membership of fallbackMemberships) await this.setCollectionMembership?.(membership);
|
|
966
|
+
return fallbackMemberships;
|
|
967
|
+
};
|
|
968
|
+
this.setCollectionMembership = (membership) => this.executeWrite((adapter) => adapter.setCollectionMembership?.(membership) ?? Promise.resolve());
|
|
969
|
+
this.removeCollectionMembership = (collectionId, itemId, scope) => this.executeWrite(
|
|
970
|
+
(adapter) => adapter.removeCollectionMembership?.(collectionId, itemId, scope) ?? Promise.resolve()
|
|
971
|
+
);
|
|
972
|
+
}
|
|
823
973
|
}
|
|
824
974
|
get isUsingFallback() {
|
|
825
975
|
return this.active === "fallback";
|
|
@@ -947,6 +1097,9 @@ function createStorageAdapter(options) {
|
|
|
947
1097
|
const getCollections = options.getCollections;
|
|
948
1098
|
const setCollection = options.setCollection;
|
|
949
1099
|
const removeCollection = options.removeCollection;
|
|
1100
|
+
const getCollectionMemberships = options.getCollectionMemberships;
|
|
1101
|
+
const setCollectionMembership = options.setCollectionMembership;
|
|
1102
|
+
const removeCollectionMembership = options.removeCollectionMembership;
|
|
950
1103
|
return {
|
|
951
1104
|
getAll: async () => options.getAll(),
|
|
952
1105
|
...getCollections && setCollection && removeCollection ? {
|
|
@@ -954,6 +1107,11 @@ function createStorageAdapter(options) {
|
|
|
954
1107
|
setCollection: async (collection) => setCollection(collection),
|
|
955
1108
|
removeCollection: async (id, scope) => removeCollection(id, scope)
|
|
956
1109
|
} : {},
|
|
1110
|
+
...getCollectionMemberships && setCollectionMembership && removeCollectionMembership ? {
|
|
1111
|
+
getCollectionMemberships: async () => getCollectionMemberships(),
|
|
1112
|
+
setCollectionMembership: async (membership) => setCollectionMembership(membership),
|
|
1113
|
+
removeCollectionMembership: async (collectionId, itemId, scope) => removeCollectionMembership(collectionId, itemId, scope)
|
|
1114
|
+
} : {},
|
|
957
1115
|
set: async (item) => options.set(item),
|
|
958
1116
|
...setMany ? { setMany: async (items) => setMany(items) } : {},
|
|
959
1117
|
remove: async (id) => options.remove(id),
|
|
@@ -1040,6 +1198,47 @@ var LocalStorageAdapter = class {
|
|
|
1040
1198
|
current.filter((entry) => entry.id !== id || !isSameKeepScope(entry.scope, scope)),
|
|
1041
1199
|
"remove"
|
|
1042
1200
|
);
|
|
1201
|
+
const memberships = await this.getCollectionMemberships();
|
|
1202
|
+
await this.writeMemberships(
|
|
1203
|
+
memberships.filter((membership) => membership.collectionId !== id || !isSameKeepScope(membership.scope, scope)),
|
|
1204
|
+
"remove"
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
async getCollectionMemberships() {
|
|
1208
|
+
if (!this.storage) return [];
|
|
1209
|
+
const storageKey = `${this.storageKey}:memberships`;
|
|
1210
|
+
let raw;
|
|
1211
|
+
try {
|
|
1212
|
+
raw = this.storage.getItem(storageKey);
|
|
1213
|
+
} catch (cause) {
|
|
1214
|
+
throw new KeepStorageAccessError({ operation: "getAll", storageKey, cause });
|
|
1215
|
+
}
|
|
1216
|
+
if (!raw) return [];
|
|
1217
|
+
try {
|
|
1218
|
+
const value = JSON.parse(raw);
|
|
1219
|
+
if (!Array.isArray(value) || !value.every(isKeepCollectionMembership)) {
|
|
1220
|
+
throw new KeepStorageParseError({ operation: "getAll", storageKey });
|
|
1221
|
+
}
|
|
1222
|
+
return value;
|
|
1223
|
+
} catch (cause) {
|
|
1224
|
+
if (cause instanceof KeepStorageParseError) throw cause;
|
|
1225
|
+
throw new KeepStorageParseError({ operation: "getAll", storageKey, cause });
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
async setCollectionMembership(membership) {
|
|
1229
|
+
if (!isKeepCollectionMembership(membership)) throw new Error("Collection membership is invalid.");
|
|
1230
|
+
const current = await this.getCollectionMemberships();
|
|
1231
|
+
const sameMembership = (entry) => entry.collectionId === membership.collectionId && entry.itemId === membership.itemId && isSameKeepScope(entry.scope, membership.scope);
|
|
1232
|
+
await this.writeMemberships([...current.filter((entry) => !sameMembership(entry)), membership], "set");
|
|
1233
|
+
}
|
|
1234
|
+
async removeCollectionMembership(collectionId, itemId, scope) {
|
|
1235
|
+
const current = await this.getCollectionMemberships();
|
|
1236
|
+
await this.writeMemberships(
|
|
1237
|
+
current.filter(
|
|
1238
|
+
(entry) => entry.collectionId !== collectionId || entry.itemId !== itemId || !isSameKeepScope(entry.scope, scope)
|
|
1239
|
+
),
|
|
1240
|
+
"remove"
|
|
1241
|
+
);
|
|
1043
1242
|
}
|
|
1044
1243
|
async set(item) {
|
|
1045
1244
|
await this.setMany([item]);
|
|
@@ -1081,7 +1280,8 @@ var LocalStorageAdapter = class {
|
|
|
1081
1280
|
subscribe(listener) {
|
|
1082
1281
|
if (typeof window === "undefined") return () => void 0;
|
|
1083
1282
|
const handleStorage = (event) => {
|
|
1084
|
-
if (event.key !== null && event.key !== this.storageKey && event.key !== `${this.storageKey}:collections`)
|
|
1283
|
+
if (event.key !== null && event.key !== this.storageKey && event.key !== `${this.storageKey}:collections` && event.key !== `${this.storageKey}:memberships`)
|
|
1284
|
+
return;
|
|
1085
1285
|
listener();
|
|
1086
1286
|
};
|
|
1087
1287
|
window.addEventListener("storage", handleStorage);
|
|
@@ -1116,6 +1316,16 @@ var LocalStorageAdapter = class {
|
|
|
1116
1316
|
throw new KeepStorageAccessError({ operation, storageKey, cause });
|
|
1117
1317
|
}
|
|
1118
1318
|
}
|
|
1319
|
+
async writeMemberships(memberships, operation) {
|
|
1320
|
+
if (!this.storage) return;
|
|
1321
|
+
const storageKey = `${this.storageKey}:memberships`;
|
|
1322
|
+
try {
|
|
1323
|
+
this.storage.setItem(storageKey, JSON.stringify(memberships));
|
|
1324
|
+
} catch (cause) {
|
|
1325
|
+
if (isQuotaExceededError(cause)) throw new KeepStorageQuotaError({ operation, storageKey, cause });
|
|
1326
|
+
throw new KeepStorageAccessError({ operation, storageKey, cause });
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1119
1329
|
};
|
|
1120
1330
|
var IndexedDBAdapter = class {
|
|
1121
1331
|
constructor(options = {}) {
|
|
@@ -1165,6 +1375,38 @@ var IndexedDBAdapter = class {
|
|
|
1165
1375
|
}
|
|
1166
1376
|
async removeCollection(id, scope) {
|
|
1167
1377
|
await this.writeCollection("remove", (store) => store.delete(collectionKey(id, scope)));
|
|
1378
|
+
const memberships = await this.getCollectionMemberships();
|
|
1379
|
+
for (const membership of memberships) {
|
|
1380
|
+
if (membership.collectionId === id && isSameKeepScope(membership.scope, scope)) {
|
|
1381
|
+
await this.removeCollectionMembership(id, membership.itemId, scope);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
async getCollectionMemberships() {
|
|
1386
|
+
const database = await this.openCollections("getAll");
|
|
1387
|
+
if (!database) return [];
|
|
1388
|
+
const storageKey = `${this.storageKey}:memberships`;
|
|
1389
|
+
try {
|
|
1390
|
+
const transaction = database.transaction("memberships", "readonly");
|
|
1391
|
+
const value = await requestToPromise2(transaction.objectStore("memberships").getAll());
|
|
1392
|
+
if (!Array.isArray(value) || !value.every((entry) => isRecord(entry) && typeof entry.key === "string" && isKeepCollectionMembership(entry))) {
|
|
1393
|
+
throw new KeepStorageParseError({ operation: "getAll", storageKey });
|
|
1394
|
+
}
|
|
1395
|
+
return value.map(({ key: _key, ...membership }) => membership);
|
|
1396
|
+
} catch (cause) {
|
|
1397
|
+
if (cause instanceof KeepStorageParseError) throw cause;
|
|
1398
|
+
throw new KeepStorageAccessError({ operation: "getAll", storageKey, cause });
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
async setCollectionMembership(membership) {
|
|
1402
|
+
if (!isKeepCollectionMembership(membership)) throw new Error("Collection membership is invalid.");
|
|
1403
|
+
await this.writeCollectionMembership(
|
|
1404
|
+
"set",
|
|
1405
|
+
(store) => store.put({ ...membership, key: membershipKey(membership.collectionId, membership.itemId, membership.scope) })
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
async removeCollectionMembership(collectionId, itemId, scope) {
|
|
1409
|
+
await this.writeCollectionMembership("remove", (store) => store.delete(membershipKey(collectionId, itemId, scope)));
|
|
1168
1410
|
}
|
|
1169
1411
|
async set(item) {
|
|
1170
1412
|
return this.setMany([item]);
|
|
@@ -1249,6 +1491,20 @@ var IndexedDBAdapter = class {
|
|
|
1249
1491
|
throw new KeepStorageAccessError({ operation, storageKey, cause });
|
|
1250
1492
|
}
|
|
1251
1493
|
}
|
|
1494
|
+
async writeCollectionMembership(operation, write) {
|
|
1495
|
+
const database = await this.openCollections(operation);
|
|
1496
|
+
if (!database) return;
|
|
1497
|
+
const storageKey = `${this.storageKey}:memberships`;
|
|
1498
|
+
try {
|
|
1499
|
+
const transaction = database.transaction("memberships", "readwrite");
|
|
1500
|
+
write(transaction.objectStore("memberships"));
|
|
1501
|
+
await transactionToPromise2(transaction);
|
|
1502
|
+
this.notifySubscribers();
|
|
1503
|
+
} catch (cause) {
|
|
1504
|
+
if (isQuotaExceededError(cause)) throw new KeepStorageQuotaError({ operation, storageKey, cause });
|
|
1505
|
+
throw new KeepStorageAccessError({ operation, storageKey, cause });
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1252
1508
|
openCollections(operation) {
|
|
1253
1509
|
if (!this.indexedDB) return Promise.resolve(void 0);
|
|
1254
1510
|
const storageKey = `${this.storageKey}:collections`;
|
|
@@ -1256,7 +1512,7 @@ var IndexedDBAdapter = class {
|
|
|
1256
1512
|
this.collectionDatabasePromise = new Promise((resolve, reject) => {
|
|
1257
1513
|
let request;
|
|
1258
1514
|
try {
|
|
1259
|
-
request = this.indexedDB?.open(storageKey,
|
|
1515
|
+
request = this.indexedDB?.open(storageKey, 2);
|
|
1260
1516
|
} catch (cause) {
|
|
1261
1517
|
reject(cause);
|
|
1262
1518
|
return;
|
|
@@ -1265,6 +1521,9 @@ var IndexedDBAdapter = class {
|
|
|
1265
1521
|
if (!request.result.objectStoreNames.contains("collections")) {
|
|
1266
1522
|
request.result.createObjectStore("collections", { keyPath: "key" });
|
|
1267
1523
|
}
|
|
1524
|
+
if (!request.result.objectStoreNames.contains("memberships")) {
|
|
1525
|
+
request.result.createObjectStore("memberships", { keyPath: "key" });
|
|
1526
|
+
}
|
|
1268
1527
|
};
|
|
1269
1528
|
request.onsuccess = () => resolve(request.result);
|
|
1270
1529
|
request.onerror = () => reject(request.error);
|
|
@@ -1307,6 +1566,9 @@ var IndexedDBAdapter = class {
|
|
|
1307
1566
|
function collectionKey(id, scope) {
|
|
1308
1567
|
return JSON.stringify([scope?.tenantId ?? null, scope?.userId ?? null, id]);
|
|
1309
1568
|
}
|
|
1569
|
+
function membershipKey(collectionId, itemId, scope) {
|
|
1570
|
+
return JSON.stringify([scope?.tenantId ?? null, scope?.userId ?? null, collectionId, itemId]);
|
|
1571
|
+
}
|
|
1310
1572
|
function getBrowserStorage2() {
|
|
1311
1573
|
if (typeof window === "undefined") return void 0;
|
|
1312
1574
|
try {
|
|
@@ -1352,6 +1614,8 @@ export {
|
|
|
1352
1614
|
removeKeepItems,
|
|
1353
1615
|
mergeKeepItemLists,
|
|
1354
1616
|
isKeepItem,
|
|
1617
|
+
isKeepCollectionDefinition,
|
|
1618
|
+
isKeepCollectionMembership,
|
|
1355
1619
|
isRecord,
|
|
1356
1620
|
getKeepScopeKey,
|
|
1357
1621
|
isSameKeepScope,
|
|
@@ -1374,4 +1638,4 @@ export {
|
|
|
1374
1638
|
LocalStorageAdapter,
|
|
1375
1639
|
IndexedDBAdapter
|
|
1376
1640
|
};
|
|
1377
|
-
//# sourceMappingURL=chunk-
|
|
1641
|
+
//# sourceMappingURL=chunk-NZLAW4LY.js.map
|