@keepkit/core 0.28.2 → 0.28.4
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 +12 -4
- package/dist/{chunk-HDML3IDX.js → chunk-2TENUU7B.js} +199 -7
- package/dist/chunk-2TENUU7B.js.map +1 -0
- package/dist/{chunk-JGGKIE3O.js → chunk-Y4U5DEGG.js} +18 -4
- package/dist/chunk-Y4U5DEGG.js.map +1 -0
- package/dist/core.d.ts +37 -6
- package/dist/core.js +183 -4
- package/dist/core.js.map +1 -1
- package/dist/react.d.ts +15 -5
- package/dist/react.js +149 -23
- package/dist/react.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/{scope-BsU6xvN9.d.ts → scope-Dtkxpnof.d.ts} +4 -1
- package/dist/storage.d.ts +22 -3
- package/dist/storage.js +1 -1
- package/dist/{store-DbAAC4yU.d.ts → store-45yaDeER.d.ts} +22 -3
- package/dist/{types-CJIbuqTO.d.ts → types-C_U5Q44o.d.ts} +10 -1
- package/package.json +1 -1
- package/dist/chunk-HDML3IDX.js.map +0 -1
- package/dist/chunk-JGGKIE3O.js.map +0 -1
package/README.md
CHANGED
|
@@ -30,7 +30,9 @@ const list = useKeepList({
|
|
|
30
30
|
|
|
31
31
|
`KeepListQuery`は`targetType`、`tags`、`search`、`sort`、`pagination`、`filter`、`savedBetween`で構成されます。`queryKeepItems`はReactなしで同じ条件を適用できます。
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
整理状態は`organization: { collection, tags, note }`の`assigned` / `unassigned`で絞り込めます。`createInboxQuery()`は未分類アイテム向けの既定queryを返します。`useKeepList()`は一括archive、pin、collection移動を提供します。Saved Viewは`KeepSavedViewStorage` / `LocalStorageKeepSavedViewStorage`でアイテムと分けて保存できます。
|
|
34
|
+
|
|
35
|
+
再発見には`useKeepItem(item).recordOpen()`で`lastOpenedAt`を記録し、`activity`の`opened`、`lastOpenedBefore`、`lastOpenedAfter`、`inactiveForMs`で絞り込めます。`inactiveForMs`は開封済みなら`lastOpenedAt`、未開封なら`savedAt`を基準に判定します。開封記録だけでは`updatedAt`を変更せず、同期・mergeでは新しいActivityを保持します。`createRediscoveryQuery()`は`never-opened`、`forgotten`、`recently-opened`のCore向けプリセットを提供し、`useKeepRediscovery()`はReact向けの一覧hookです。
|
|
34
36
|
|
|
35
37
|
保存順を巡回ルートとして管理する場合は、`reorderKeepItems` / `moveKeepItem`、Reactでは`useKeepNavigator`と`useKeepList().reorder()` / `.move()`を利用できます。`getKeepNavigationState`は現在・前・次のアイテムと進行度を返します。
|
|
36
38
|
|
|
@@ -40,7 +42,7 @@ const list = useKeepList({
|
|
|
40
42
|
|
|
41
43
|
保存対象の公開状態は`KeepItem.status`(`expired`、`removed`、`private`など)と`statusReason`で保持できます。`KeepProvider`の`validateItem` / `resolveItem`を指定すると、引数なしの`revalidateItems()`で検証できます。`revalidateItems`に`removeStatuses`を渡すと検出したアイテムを保存一覧から削除します。`SyncStorageAdapter`は`userId`、`tenantId`、`maxRetries`、`retryDelayMs`、`retryBackoff`に対応し、`retrySync()`で失敗後の同期を再開できます。
|
|
42
44
|
|
|
43
|
-
v0.28.
|
|
45
|
+
v0.28.4では、空のコレクションと変更した名前をブラウザー内に永続化し、再読み込み後に復元できるようになりました。Inbox、Saved View、利用履歴、Rediscovery query、URL状態codec、認証付き同期も利用できます。
|
|
44
46
|
|
|
45
47
|
`createAuthenticatedSyncKit`は、リクエストごとの`getAuthToken`、注入可能なpush/pull transport、401/403時の再認証callback、永続オフラインキュー、`setScope`による安全なユーザー/テナント切替を提供します。詳細は[`examples/authenticated-sync`](../../examples/authenticated-sync/README.md)を参照してください。
|
|
46
48
|
|
|
@@ -77,7 +79,9 @@ const list = useKeepList({
|
|
|
77
79
|
|
|
78
80
|
`KeepItemInput` contains `id`, `meta`, `targetType`, `note`, `tags`, and the optional persisted `order`. KeepKit owns persistence timestamps and tag normalization. `KeepListQuery` uses the canonical `targetType`, `tags`, `search`, `sort`, `pagination`, `filter`, and `savedBetween` fields.
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
Filter organization with `organization: { collection, tags, note }`, each set to `assigned` or `unassigned`. `createInboxQuery()` returns the default unassigned query. `useKeepList()` includes batch archive, pin, and collection mutations. Saved Views use `KeepSavedViewStorage` and `LocalStorageKeepSavedViewStorage`, separate from item storage.
|
|
83
|
+
|
|
84
|
+
For rediscovery, call `useKeepItem(item).recordOpen()` to persist `lastOpenedAt`, then filter with `activity.opened`, `lastOpenedBefore`, `lastOpenedAfter`, or `inactiveForMs`. `inactiveForMs` uses `lastOpenedAt` for opened items and `savedAt` for never-opened items. Recording an open does not change `updatedAt`, and sync/merge preserves newer activity independently. `createRediscoveryQuery()` provides framework-neutral `never-opened`, `forgotten`, and `recently-opened` presets, while `useKeepRediscovery()` provides the React list hook.
|
|
81
85
|
|
|
82
86
|
Use `reorderKeepItems` / `moveKeepItem` for framework-neutral route ordering, or `useKeepNavigator` with `useKeepList().reorder()` / `.move()` in React. `getKeepNavigationState` returns the current, previous, next, and progress state.
|
|
83
87
|
|
|
@@ -87,7 +91,7 @@ Use `@keepkit/core/core` for framework-neutral code, `@keepkit/core/react` for R
|
|
|
87
91
|
|
|
88
92
|
`KeepItem.status` and `statusReason` preserve source availability such as `expired`, `removed`, and `private`. Configure `KeepProvider` with `validateItem` / `resolveItem` to make `revalidateItems()` use those hooks by default. Pass `removeStatuses` to remove detected items from storage. `SyncStorageAdapter` supports scoped queues with `userId` and `tenantId`, configurable retries/backoff, and explicit `retrySync()` recovery.
|
|
89
93
|
|
|
90
|
-
v0.28.
|
|
94
|
+
v0.28.4 persists empty collections and renamed labels in browser storage so they survive reloads. Inbox, Saved Views, activity tracking, Rediscovery queries, URL state codecs, user/tenant isolation, and authenticated sync are also available.
|
|
91
95
|
|
|
92
96
|
`createAuthenticatedSyncKit` provides a per-request `getAuthToken`, injectable push/pull transport, 401/403 reauthentication callbacks, persistent offline queues, and `setScope` for safe user or tenant changes. See [`examples/authenticated-sync`](../../examples/authenticated-sync/README.md) for a recipe.
|
|
93
97
|
|
|
@@ -97,6 +101,10 @@ The v0.5 factory returns `Provider`, `Button`, `useContext`, `useItem`, `useList
|
|
|
97
101
|
|
|
98
102
|
`KeepItem` and `KeepItemInput` accept optional `archived`, `pinned`, and `collectionId` fields. `useKeepList` defaults to unarchived items; pass `archived: true` for the archive, `archiveScope: "all"` for both scopes, `collectionId` for an exact collection filter, and `pinnedFirst: true` to stably promote pinned items without changing the existing order inside either group. `useKeepCollections({ targetType, orderBy })` derives de-duplicated collection IDs, names, and counts from the complete saved-item snapshot. `useKeepItem` and the provider expose `toggleArchive`, `archiveItem`, `unarchiveItem`, `togglePin`, and `moveToCollection` operations. Each operation updates `updatedAt`, removes an empty collection ID, and uses the normal persistence, rollback, plugin, and `onChange` pipeline.
|
|
99
103
|
|
|
104
|
+
The standard browser adapters persist explicit collection definitions, including empty collections and renamed labels, separately from items. A custom `StorageAdapter` can opt in by implementing `getCollections`, `setCollection`, and `removeCollection`; without them, collection management remains in memory. `clear()` and JSON backups still affect items only, and collection definitions do not enter remote sync.
|
|
105
|
+
|
|
100
106
|
## アーカイブ・ピン留め・コレクション
|
|
101
107
|
|
|
102
108
|
`KeepItem` と `KeepItemInput` は `archived`、`pinned`、`collectionId` を任意で受け取れます。`useKeepList` は未アーカイブを既定とし、`archived: true` でアーカイブを、`archiveScope: "all"` で両方を、`collectionId` で完全一致のコレクションを取得できます。`useKeepCollections({ targetType, orderBy })` は全保存アイテムからコレクションID・名前・件数を重複なく導出します。`pinnedFirst: true` は既存の順序を保ったままピン留め項目を先頭へ安定移動します。`useKeepItem` と Provider には `toggleArchive`、`archiveItem`、`unarchiveItem`、`togglePin`、`moveToCollection` を追加しました。各操作は `updatedAt` を更新し、空のコレクション ID はプロパティを削除して、既存の永続化・rollback・plugin・`onChange` 経路を利用します。
|
|
109
|
+
|
|
110
|
+
標準ブラウザーストレージは、空のコレクションと変更した名前をアイテムとは別に永続化します。独自の`StorageAdapter`では`getCollections`、`setCollection`、`removeCollection`を実装すると永続化でき、未実装時は従来どおり画面内の状態として扱います。`clear()`とJSONバックアップは引き続きアイテムのみを対象とし、コレクション定義はリモート同期に含まれません。
|
|
@@ -64,7 +64,13 @@ function mergeKeepItemLists(remoteItems, localItems) {
|
|
|
64
64
|
const byId = new Map(remoteItems.map((item) => [item.id, item]));
|
|
65
65
|
for (const localItem of localItems) {
|
|
66
66
|
const remoteItem = byId.get(localItem.id);
|
|
67
|
-
if (!remoteItem
|
|
67
|
+
if (!remoteItem) {
|
|
68
|
+
byId.set(localItem.id, localItem);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const content = localItem.updatedAt > remoteItem.updatedAt ? localItem : remoteItem;
|
|
72
|
+
const lastOpenedAt = remoteItem.lastOpenedAt === void 0 ? localItem.lastOpenedAt : localItem.lastOpenedAt === void 0 ? remoteItem.lastOpenedAt : Math.max(remoteItem.lastOpenedAt, localItem.lastOpenedAt);
|
|
73
|
+
byId.set(localItem.id, lastOpenedAt === void 0 ? content : { ...content, lastOpenedAt });
|
|
68
74
|
}
|
|
69
75
|
return [...byId.values()].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
70
76
|
}
|
|
@@ -75,6 +81,9 @@ function isKeepItem(value) {
|
|
|
75
81
|
function isKeepItemArray(value) {
|
|
76
82
|
return Array.isArray(value) && value.every(isKeepItem);
|
|
77
83
|
}
|
|
84
|
+
function isKeepCollectionDefinition(value) {
|
|
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
|
+
}
|
|
78
87
|
function isRecord(value) {
|
|
79
88
|
return typeof value === "object" && value !== null;
|
|
80
89
|
}
|
|
@@ -96,6 +105,17 @@ var ScopedStorageAdapter = class {
|
|
|
96
105
|
this.scope = scope;
|
|
97
106
|
const scopeKey = getKeepScopeKey(scope);
|
|
98
107
|
this.storageKey = base.storageKey ? base.storageKey.endsWith(scopeKey) && scopeKey ? base.storageKey : `${base.storageKey}${scopeKey}` : void 0;
|
|
108
|
+
if (base.getCollections && base.setCollection && base.removeCollection) {
|
|
109
|
+
const getCollections = base.getCollections.bind(base);
|
|
110
|
+
const setCollection = base.setCollection.bind(base);
|
|
111
|
+
const removeCollection = base.removeCollection.bind(base);
|
|
112
|
+
this.getCollections = async () => {
|
|
113
|
+
const collections = await getCollections();
|
|
114
|
+
return this.scope ? collections.filter((collection) => isSameKeepScope(collection.scope, this.scope)) : collections;
|
|
115
|
+
};
|
|
116
|
+
this.setCollection = (collection) => setCollection({ ...collection, ...this.scope ? { scope: this.scope } : {} });
|
|
117
|
+
this.removeCollection = (id) => removeCollection(id, this.scope);
|
|
118
|
+
}
|
|
99
119
|
}
|
|
100
120
|
async getAll() {
|
|
101
121
|
const items = await this.base.getAll();
|
|
@@ -356,6 +376,18 @@ var SyncStorageAdapter = class {
|
|
|
356
376
|
this.retryDelayMs = Math.max(0, options.retryDelayMs ?? 0);
|
|
357
377
|
this.retryBackoff = Math.max(1, options.retryBackoff ?? 2);
|
|
358
378
|
this.storageKey = this.local.storageKey;
|
|
379
|
+
if (this.local.getCollections && this.local.setCollection && this.local.removeCollection) {
|
|
380
|
+
const getCollections = this.local.getCollections.bind(this.local);
|
|
381
|
+
const setCollection = this.local.setCollection.bind(this.local);
|
|
382
|
+
const removeCollection = this.local.removeCollection.bind(this.local);
|
|
383
|
+
this.getCollections = async () => {
|
|
384
|
+
const collections = await getCollections();
|
|
385
|
+
const scope = this.scope;
|
|
386
|
+
return scope ? collections.filter((collection) => sameScope(collection.scope, scope)) : collections;
|
|
387
|
+
};
|
|
388
|
+
this.setCollection = (collection) => setCollection({ ...collection, ...this.scope ? { scope: this.scope } : {} });
|
|
389
|
+
this.removeCollection = (id) => removeCollection(id, this.scope);
|
|
390
|
+
}
|
|
359
391
|
if (typeof window !== "undefined") {
|
|
360
392
|
this.onlineHandler = () => void this.flushSync();
|
|
361
393
|
window.addEventListener("online", this.onlineHandler);
|
|
@@ -644,11 +676,21 @@ var SyncStorageAdapter = class {
|
|
|
644
676
|
const pendingIds = new Set(this.queueItems.map((operation) => operation.id));
|
|
645
677
|
const localItems = await this.getAll();
|
|
646
678
|
const localById = new Map(localItems.map((item) => [item.id, item]));
|
|
647
|
-
const
|
|
648
|
-
|
|
679
|
+
const remoteById = /* @__PURE__ */ new Map();
|
|
680
|
+
for (const item of remoteItems) {
|
|
681
|
+
if (this.scope && item.scope && !sameScope(item.scope, this.scope)) continue;
|
|
682
|
+
if (pendingIds.has(item.id)) continue;
|
|
683
|
+
const previous = remoteById.get(item.id);
|
|
684
|
+
remoteById.set(item.id, previous ? mergeKeepItemLists([item], [previous])[0] ?? item : item);
|
|
685
|
+
}
|
|
686
|
+
const incoming = [...remoteById.values()].flatMap((item) => {
|
|
649
687
|
const current = localById.get(item.id);
|
|
650
|
-
|
|
651
|
-
|
|
688
|
+
if (current && item.updatedAt < current.updatedAt && (item.lastOpenedAt ?? 0) <= (current.lastOpenedAt ?? 0)) {
|
|
689
|
+
return [];
|
|
690
|
+
}
|
|
691
|
+
const merged = current ? mergeKeepItemLists([item], [current])[0] ?? item : item;
|
|
692
|
+
return [this.applyScope(merged)];
|
|
693
|
+
});
|
|
652
694
|
if (incoming.length === 0) return true;
|
|
653
695
|
await persistKeepItems(this.local, incoming);
|
|
654
696
|
this.notifyDataListeners();
|
|
@@ -755,6 +797,29 @@ var FallbackStorageAdapter = class {
|
|
|
755
797
|
this.migrateFallbackOnEmpty = options.migrateFallbackOnEmpty ?? false;
|
|
756
798
|
this.mirrorWrites = options.mirrorWrites ?? false;
|
|
757
799
|
this.storageKey = options.fallback.storageKey ?? options.primary.storageKey;
|
|
800
|
+
if (this.primary.getCollections && this.primary.setCollection && this.primary.removeCollection && this.fallback.getCollections && this.fallback.setCollection && this.fallback.removeCollection) {
|
|
801
|
+
const fallbackGetCollections = this.fallback.getCollections.bind(this.fallback);
|
|
802
|
+
this.getCollections = async () => {
|
|
803
|
+
const collections = await this.execute((adapter) => {
|
|
804
|
+
if (!adapter.getCollections) throw new Error("Collection storage is unavailable.");
|
|
805
|
+
return adapter.getCollections();
|
|
806
|
+
});
|
|
807
|
+
if (this.active === "primary" && this.migrateFallbackOnEmpty && collections.length === 0) {
|
|
808
|
+
const fallbackCollections = await fallbackGetCollections();
|
|
809
|
+
for (const collection of fallbackCollections) await this.setCollection?.(collection);
|
|
810
|
+
return fallbackCollections;
|
|
811
|
+
}
|
|
812
|
+
return collections;
|
|
813
|
+
};
|
|
814
|
+
this.setCollection = (collection) => this.executeWrite((adapter) => {
|
|
815
|
+
if (!adapter.setCollection) throw new Error("Collection storage is unavailable.");
|
|
816
|
+
return adapter.setCollection(collection);
|
|
817
|
+
});
|
|
818
|
+
this.removeCollection = (id, scope) => this.executeWrite((adapter) => {
|
|
819
|
+
if (!adapter.removeCollection) throw new Error("Collection storage is unavailable.");
|
|
820
|
+
return adapter.removeCollection(id, scope);
|
|
821
|
+
});
|
|
822
|
+
}
|
|
758
823
|
}
|
|
759
824
|
get isUsingFallback() {
|
|
760
825
|
return this.active === "fallback";
|
|
@@ -879,8 +944,16 @@ function createStorageAdapter(options) {
|
|
|
879
944
|
const subscribe = options.subscribe;
|
|
880
945
|
const setMany = options.setMany;
|
|
881
946
|
const removeMany = options.removeMany;
|
|
947
|
+
const getCollections = options.getCollections;
|
|
948
|
+
const setCollection = options.setCollection;
|
|
949
|
+
const removeCollection = options.removeCollection;
|
|
882
950
|
return {
|
|
883
951
|
getAll: async () => options.getAll(),
|
|
952
|
+
...getCollections && setCollection && removeCollection ? {
|
|
953
|
+
getCollections: async () => getCollections(),
|
|
954
|
+
setCollection: async (collection) => setCollection(collection),
|
|
955
|
+
removeCollection: async (id, scope) => removeCollection(id, scope)
|
|
956
|
+
} : {},
|
|
884
957
|
set: async (item) => options.set(item),
|
|
885
958
|
...setMany ? { setMany: async (items) => setMany(items) } : {},
|
|
886
959
|
remove: async (id) => options.remove(id),
|
|
@@ -929,6 +1002,45 @@ var LocalStorageAdapter = class {
|
|
|
929
1002
|
});
|
|
930
1003
|
}
|
|
931
1004
|
}
|
|
1005
|
+
async getCollections() {
|
|
1006
|
+
if (!this.storage) return [];
|
|
1007
|
+
const storageKey = `${this.storageKey}:collections`;
|
|
1008
|
+
let raw;
|
|
1009
|
+
try {
|
|
1010
|
+
raw = this.storage.getItem(storageKey);
|
|
1011
|
+
} catch (cause) {
|
|
1012
|
+
throw new KeepStorageAccessError({ operation: "getAll", storageKey, cause });
|
|
1013
|
+
}
|
|
1014
|
+
if (!raw) return [];
|
|
1015
|
+
try {
|
|
1016
|
+
const value = JSON.parse(raw);
|
|
1017
|
+
if (!Array.isArray(value) || !value.every(isKeepCollectionDefinition)) {
|
|
1018
|
+
throw new KeepStorageParseError({ operation: "getAll", storageKey });
|
|
1019
|
+
}
|
|
1020
|
+
return value;
|
|
1021
|
+
} catch (cause) {
|
|
1022
|
+
if (cause instanceof KeepStorageParseError) throw cause;
|
|
1023
|
+
throw new KeepStorageParseError({ operation: "getAll", storageKey, cause });
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
async setCollection(collection) {
|
|
1027
|
+
if (!isKeepCollectionDefinition(collection)) throw new Error("Collection id and name are required.");
|
|
1028
|
+
const current = await this.getCollections();
|
|
1029
|
+
await this.writeCollections(
|
|
1030
|
+
[
|
|
1031
|
+
...current.filter((entry) => entry.id !== collection.id || !isSameKeepScope(entry.scope, collection.scope)),
|
|
1032
|
+
collection
|
|
1033
|
+
],
|
|
1034
|
+
"set"
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
async removeCollection(id, scope) {
|
|
1038
|
+
const current = await this.getCollections();
|
|
1039
|
+
await this.writeCollections(
|
|
1040
|
+
current.filter((entry) => entry.id !== id || !isSameKeepScope(entry.scope, scope)),
|
|
1041
|
+
"remove"
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
932
1044
|
async set(item) {
|
|
933
1045
|
await this.setMany([item]);
|
|
934
1046
|
}
|
|
@@ -969,7 +1081,7 @@ var LocalStorageAdapter = class {
|
|
|
969
1081
|
subscribe(listener) {
|
|
970
1082
|
if (typeof window === "undefined") return () => void 0;
|
|
971
1083
|
const handleStorage = (event) => {
|
|
972
|
-
if (event.key !== null && event.key !== this.storageKey) return;
|
|
1084
|
+
if (event.key !== null && event.key !== this.storageKey && event.key !== `${this.storageKey}:collections`) return;
|
|
973
1085
|
listener();
|
|
974
1086
|
};
|
|
975
1087
|
window.addEventListener("storage", handleStorage);
|
|
@@ -994,6 +1106,16 @@ var LocalStorageAdapter = class {
|
|
|
994
1106
|
});
|
|
995
1107
|
}
|
|
996
1108
|
}
|
|
1109
|
+
async writeCollections(collections, operation) {
|
|
1110
|
+
if (!this.storage) return;
|
|
1111
|
+
const storageKey = `${this.storageKey}:collections`;
|
|
1112
|
+
try {
|
|
1113
|
+
this.storage.setItem(storageKey, JSON.stringify(collections));
|
|
1114
|
+
} catch (cause) {
|
|
1115
|
+
if (isQuotaExceededError(cause)) throw new KeepStorageQuotaError({ operation, storageKey, cause });
|
|
1116
|
+
throw new KeepStorageAccessError({ operation, storageKey, cause });
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
997
1119
|
};
|
|
998
1120
|
var IndexedDBAdapter = class {
|
|
999
1121
|
constructor(options = {}) {
|
|
@@ -1018,6 +1140,32 @@ var IndexedDBAdapter = class {
|
|
|
1018
1140
|
throw new KeepStorageAccessError({ operation: "getAll", storageKey: this.storageKey, cause });
|
|
1019
1141
|
}
|
|
1020
1142
|
}
|
|
1143
|
+
async getCollections() {
|
|
1144
|
+
const database = await this.openCollections("getAll");
|
|
1145
|
+
if (!database) return [];
|
|
1146
|
+
const storageKey = `${this.storageKey}:collections`;
|
|
1147
|
+
try {
|
|
1148
|
+
const transaction = database.transaction("collections", "readonly");
|
|
1149
|
+
const value = await requestToPromise2(transaction.objectStore("collections").getAll());
|
|
1150
|
+
if (!Array.isArray(value) || !value.every((entry) => isRecord(entry) && typeof entry.key === "string" && isKeepCollectionDefinition(entry))) {
|
|
1151
|
+
throw new KeepStorageParseError({ operation: "getAll", storageKey });
|
|
1152
|
+
}
|
|
1153
|
+
return value.map(({ key: _key, ...collection }) => collection);
|
|
1154
|
+
} catch (cause) {
|
|
1155
|
+
if (cause instanceof KeepStorageParseError) throw cause;
|
|
1156
|
+
throw new KeepStorageAccessError({ operation: "getAll", storageKey, cause });
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
async setCollection(collection) {
|
|
1160
|
+
if (!isKeepCollectionDefinition(collection)) throw new Error("Collection id and name are required.");
|
|
1161
|
+
await this.writeCollection(
|
|
1162
|
+
"set",
|
|
1163
|
+
(store) => store.put({ ...collection, key: collectionKey(collection.id, collection.scope) })
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
async removeCollection(id, scope) {
|
|
1167
|
+
await this.writeCollection("remove", (store) => store.delete(collectionKey(id, scope)));
|
|
1168
|
+
}
|
|
1021
1169
|
async set(item) {
|
|
1022
1170
|
return this.setMany([item]);
|
|
1023
1171
|
}
|
|
@@ -1087,6 +1235,47 @@ var IndexedDBAdapter = class {
|
|
|
1087
1235
|
channel.postMessage({ type: "keepkit:changed" });
|
|
1088
1236
|
channel.close();
|
|
1089
1237
|
}
|
|
1238
|
+
async writeCollection(operation, write) {
|
|
1239
|
+
const database = await this.openCollections(operation);
|
|
1240
|
+
if (!database) return;
|
|
1241
|
+
const storageKey = `${this.storageKey}:collections`;
|
|
1242
|
+
try {
|
|
1243
|
+
const transaction = database.transaction("collections", "readwrite");
|
|
1244
|
+
write(transaction.objectStore("collections"));
|
|
1245
|
+
await transactionToPromise2(transaction);
|
|
1246
|
+
this.notifySubscribers();
|
|
1247
|
+
} catch (cause) {
|
|
1248
|
+
if (isQuotaExceededError(cause)) throw new KeepStorageQuotaError({ operation, storageKey, cause });
|
|
1249
|
+
throw new KeepStorageAccessError({ operation, storageKey, cause });
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
openCollections(operation) {
|
|
1253
|
+
if (!this.indexedDB) return Promise.resolve(void 0);
|
|
1254
|
+
const storageKey = `${this.storageKey}:collections`;
|
|
1255
|
+
if (!this.collectionDatabasePromise) {
|
|
1256
|
+
this.collectionDatabasePromise = new Promise((resolve, reject) => {
|
|
1257
|
+
let request;
|
|
1258
|
+
try {
|
|
1259
|
+
request = this.indexedDB?.open(storageKey, 1);
|
|
1260
|
+
} catch (cause) {
|
|
1261
|
+
reject(cause);
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
request.onupgradeneeded = () => {
|
|
1265
|
+
if (!request.result.objectStoreNames.contains("collections")) {
|
|
1266
|
+
request.result.createObjectStore("collections", { keyPath: "key" });
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
request.onsuccess = () => resolve(request.result);
|
|
1270
|
+
request.onerror = () => reject(request.error);
|
|
1271
|
+
request.onblocked = () => reject(request.error ?? new Error("IndexedDB collection storage open was blocked."));
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
return this.collectionDatabasePromise.catch((cause) => {
|
|
1275
|
+
this.collectionDatabasePromise = void 0;
|
|
1276
|
+
throw new KeepStorageAccessError({ operation, storageKey, cause });
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1090
1279
|
open(operation) {
|
|
1091
1280
|
if (!this.indexedDB) return Promise.resolve(void 0);
|
|
1092
1281
|
if (!this.databasePromise) {
|
|
@@ -1115,6 +1304,9 @@ var IndexedDBAdapter = class {
|
|
|
1115
1304
|
});
|
|
1116
1305
|
}
|
|
1117
1306
|
};
|
|
1307
|
+
function collectionKey(id, scope) {
|
|
1308
|
+
return JSON.stringify([scope?.tenantId ?? null, scope?.userId ?? null, id]);
|
|
1309
|
+
}
|
|
1118
1310
|
function getBrowserStorage2() {
|
|
1119
1311
|
if (typeof window === "undefined") return void 0;
|
|
1120
1312
|
try {
|
|
@@ -1182,4 +1374,4 @@ export {
|
|
|
1182
1374
|
LocalStorageAdapter,
|
|
1183
1375
|
IndexedDBAdapter
|
|
1184
1376
|
};
|
|
1185
|
-
//# sourceMappingURL=chunk-
|
|
1377
|
+
//# sourceMappingURL=chunk-2TENUU7B.js.map
|