@keepkit/core 0.10.0 → 0.12.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/README.md +8 -0
- package/dist/{chunk-XF5VI6O5.js → chunk-MDTL5L64.js} +1 -1
- package/dist/chunk-MDTL5L64.js.map +1 -0
- package/dist/{chunk-EXE4E3GA.js → chunk-VJDO3GFH.js} +186 -9
- package/dist/chunk-VJDO3GFH.js.map +1 -0
- package/dist/core.d.ts +71 -5
- package/dist/core.js +305 -2
- package/dist/core.js.map +1 -1
- package/dist/react.d.ts +19 -7
- package/dist/react.js +154 -9
- package/dist/react.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/scope-y_aBm363.d.ts +38 -0
- package/dist/storage.d.ts +11 -3
- package/dist/storage.js +7 -2
- package/dist/{types--YahIoEB.d.ts → types-iMK12pmy.d.ts} +35 -4
- package/dist/{store-Cgqx7_xd.d.ts → url-VhisKZCR.d.ts} +39 -2
- package/package.json +1 -1
- package/dist/chunk-5QSZP6MT.js +0 -1
- package/dist/chunk-5QSZP6MT.js.map +0 -1
- package/dist/chunk-EXE4E3GA.js.map +0 -1
- package/dist/chunk-XF5VI6O5.js.map +0 -1
package/dist/react.js
CHANGED
|
@@ -6,15 +6,14 @@ import {
|
|
|
6
6
|
isKeepItemMetadataStale,
|
|
7
7
|
queryKeepItems,
|
|
8
8
|
revalidateKeepItems
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MDTL5L64.js";
|
|
10
10
|
import {
|
|
11
11
|
parseKeepMeta
|
|
12
12
|
} from "./chunk-THZ3ACR2.js";
|
|
13
|
-
import "./chunk-5QSZP6MT.js";
|
|
14
13
|
import {
|
|
15
14
|
createBrowserStorageAdapter,
|
|
16
15
|
normalizeKeepTags
|
|
17
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VJDO3GFH.js";
|
|
18
17
|
|
|
19
18
|
// src/hooks/useKeepItem.ts
|
|
20
19
|
import { useCallback as useCallback3 } from "react";
|
|
@@ -69,6 +68,7 @@ function KeepProvider({
|
|
|
69
68
|
onNoteUpdate,
|
|
70
69
|
onTagsUpdate,
|
|
71
70
|
onChange,
|
|
71
|
+
onUndo,
|
|
72
72
|
onError,
|
|
73
73
|
plugins = [],
|
|
74
74
|
schemaVersion,
|
|
@@ -81,6 +81,8 @@ function KeepProvider({
|
|
|
81
81
|
boundaryResetKey,
|
|
82
82
|
validateItem,
|
|
83
83
|
resolveItem,
|
|
84
|
+
autoRevalidation,
|
|
85
|
+
undoTimeoutMs,
|
|
84
86
|
children
|
|
85
87
|
}) {
|
|
86
88
|
const content = /* @__PURE__ */ jsx(
|
|
@@ -93,6 +95,7 @@ function KeepProvider({
|
|
|
93
95
|
onNoteUpdate,
|
|
94
96
|
onTagsUpdate,
|
|
95
97
|
onChange,
|
|
98
|
+
onUndo,
|
|
96
99
|
onError,
|
|
97
100
|
plugins,
|
|
98
101
|
schemaVersion,
|
|
@@ -102,6 +105,8 @@ function KeepProvider({
|
|
|
102
105
|
migrateMeta,
|
|
103
106
|
validateItem,
|
|
104
107
|
resolveItem,
|
|
108
|
+
autoRevalidation,
|
|
109
|
+
undoTimeoutMs,
|
|
105
110
|
children
|
|
106
111
|
}
|
|
107
112
|
);
|
|
@@ -116,6 +121,7 @@ function KeepProviderContent({
|
|
|
116
121
|
onNoteUpdate,
|
|
117
122
|
onTagsUpdate,
|
|
118
123
|
onChange,
|
|
124
|
+
onUndo,
|
|
119
125
|
onError,
|
|
120
126
|
plugins = [],
|
|
121
127
|
schemaVersion,
|
|
@@ -125,6 +131,8 @@ function KeepProviderContent({
|
|
|
125
131
|
migrateMeta,
|
|
126
132
|
validateItem,
|
|
127
133
|
resolveItem,
|
|
134
|
+
autoRevalidation,
|
|
135
|
+
undoTimeoutMs = 5e3,
|
|
128
136
|
children
|
|
129
137
|
}) {
|
|
130
138
|
const storeRef = useRef(null);
|
|
@@ -135,17 +143,19 @@ function KeepProviderContent({
|
|
|
135
143
|
isHydrated: false,
|
|
136
144
|
isMutating: false,
|
|
137
145
|
error: null,
|
|
138
|
-
lastChange: void 0
|
|
146
|
+
lastChange: void 0,
|
|
147
|
+
undo: { canUndo: false, ids: [] }
|
|
139
148
|
});
|
|
140
149
|
}
|
|
141
150
|
const store = storeRef.current;
|
|
142
151
|
const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
143
|
-
const { items, isLoading, isHydrated, isMutating, error, lastChange } = state;
|
|
152
|
+
const { items, isLoading, isHydrated, isMutating, error, lastChange, undo: storedUndo } = state;
|
|
153
|
+
const undo = storedUndo ?? EMPTY_UNDO_STATE;
|
|
144
154
|
const itemsRef = useRef(items);
|
|
145
155
|
const pluginsRef = useRef(plugins);
|
|
146
156
|
pluginsRef.current = plugins;
|
|
147
|
-
const handlersRef = useRef({ onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onError });
|
|
148
|
-
handlersRef.current = { onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onError };
|
|
157
|
+
const handlersRef = useRef({ onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onUndo, onError });
|
|
158
|
+
handlersRef.current = { onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onUndo, onError };
|
|
149
159
|
const migrationRef = useRef({
|
|
150
160
|
schemaVersion,
|
|
151
161
|
migrateMeta,
|
|
@@ -157,6 +167,10 @@ function KeepProviderContent({
|
|
|
157
167
|
const operationTailRef = useRef(Promise.resolve());
|
|
158
168
|
const pendingRefreshesRef = useRef(0);
|
|
159
169
|
const pendingMutationsRef = useRef(0);
|
|
170
|
+
const undoRef = useRef(void 0);
|
|
171
|
+
const autoRevalidationRef = useRef(autoRevalidation);
|
|
172
|
+
autoRevalidationRef.current = autoRevalidation;
|
|
173
|
+
const didMountRevalidateRef = useRef(false);
|
|
160
174
|
const syncStorage = isSyncCapableStorage(storage) ? storage : void 0;
|
|
161
175
|
const getSyncState = useCallback(() => syncStorage?.getSyncState() ?? IDLE_SYNC_STATE, [syncStorage]);
|
|
162
176
|
const subscribeSync = useCallback(
|
|
@@ -257,6 +271,16 @@ function KeepProviderContent({
|
|
|
257
271
|
if (!storage.subscribe) return;
|
|
258
272
|
return storage.subscribe(() => void refresh());
|
|
259
273
|
}, [refresh, storage]);
|
|
274
|
+
useEffect(() => {
|
|
275
|
+
if (!isScopeAwareStorage(storage)) return;
|
|
276
|
+
return storage.subscribeScope(() => {
|
|
277
|
+
undoRef.current?.timer && clearTimeout(undoRef.current.timer);
|
|
278
|
+
undoRef.current = void 0;
|
|
279
|
+
itemsRef.current = [];
|
|
280
|
+
store.setState({ items: [], isHydrated: false, isLoading: true, error: null, undo: EMPTY_UNDO_STATE });
|
|
281
|
+
void refresh();
|
|
282
|
+
});
|
|
283
|
+
}, [refresh, storage, store]);
|
|
260
284
|
const runMutation = useCallback(
|
|
261
285
|
(action, id, createPlan) => {
|
|
262
286
|
pendingMutationsRef.current += 1;
|
|
@@ -474,6 +498,69 @@ function KeepProviderContent({
|
|
|
474
498
|
},
|
|
475
499
|
[runMutation, storage]
|
|
476
500
|
);
|
|
501
|
+
const rememberUndo = useCallback(
|
|
502
|
+
(removedItems) => {
|
|
503
|
+
undoRef.current?.timer && clearTimeout(undoRef.current.timer);
|
|
504
|
+
const expiresAt = Date.now() + Math.max(0, undoTimeoutMs);
|
|
505
|
+
const timer = setTimeout(
|
|
506
|
+
() => {
|
|
507
|
+
undoRef.current = void 0;
|
|
508
|
+
store.setState({ undo: { canUndo: false, ids: [] } });
|
|
509
|
+
},
|
|
510
|
+
Math.max(0, undoTimeoutMs)
|
|
511
|
+
);
|
|
512
|
+
undoRef.current = { items: removedItems, expiresAt, timer };
|
|
513
|
+
store.setState({ undo: { canUndo: true, ids: removedItems.map((item) => item.id), expiresAt } });
|
|
514
|
+
},
|
|
515
|
+
[store, undoTimeoutMs]
|
|
516
|
+
);
|
|
517
|
+
const removeItemWithUndo = useCallback(
|
|
518
|
+
async (id) => {
|
|
519
|
+
const item = itemsRef.current.find((current) => current.id === id);
|
|
520
|
+
await removeItem(id);
|
|
521
|
+
if (item) rememberUndo([item]);
|
|
522
|
+
},
|
|
523
|
+
[rememberUndo, removeItem]
|
|
524
|
+
);
|
|
525
|
+
const removeItemsWithUndo = useCallback(
|
|
526
|
+
async (ids) => {
|
|
527
|
+
const idSet = new Set(ids);
|
|
528
|
+
const removedItems = itemsRef.current.filter((item) => idSet.has(item.id));
|
|
529
|
+
await removeItems(ids);
|
|
530
|
+
if (removedItems.length > 0) rememberUndo(removedItems);
|
|
531
|
+
},
|
|
532
|
+
[rememberUndo, removeItems]
|
|
533
|
+
);
|
|
534
|
+
const undoLastRemoval = useCallback(async () => {
|
|
535
|
+
const pending = undoRef.current;
|
|
536
|
+
if (!pending || pending.expiresAt < Date.now()) {
|
|
537
|
+
undoRef.current = void 0;
|
|
538
|
+
store.setState({ undo: { canUndo: false, ids: [] } });
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if (pending.timer) clearTimeout(pending.timer);
|
|
542
|
+
undoRef.current = void 0;
|
|
543
|
+
store.setState({ undo: { canUndo: false, ids: [] } });
|
|
544
|
+
await runMutation("undo", void 0, (previous) => {
|
|
545
|
+
const restored = new Map(pending.items.map((item) => [item.id, item]));
|
|
546
|
+
const next = [...previous.filter((item) => !restored.has(item.id)), ...pending.items].sort(
|
|
547
|
+
(a, b) => b.updatedAt - a.updatedAt
|
|
548
|
+
);
|
|
549
|
+
return {
|
|
550
|
+
next,
|
|
551
|
+
persist: async () => {
|
|
552
|
+
if (storage.setMany) await storage.setMany(pending.items);
|
|
553
|
+
else for (const item of pending.items) await storage.set(item);
|
|
554
|
+
},
|
|
555
|
+
onSuccess: () => handlersRef.current.onUndo?.(pending.items),
|
|
556
|
+
pluginContext: { action: "undo", items: pending.items }
|
|
557
|
+
};
|
|
558
|
+
});
|
|
559
|
+
}, [runMutation, storage, store]);
|
|
560
|
+
useEffect(() => {
|
|
561
|
+
if (syncState.status !== "error" || !undoRef.current) return;
|
|
562
|
+
void undoLastRemoval();
|
|
563
|
+
}, [syncState.status, undoLastRemoval]);
|
|
477
564
|
const clear = useCallback(
|
|
478
565
|
() => runMutation("clear", void 0, (_previous) => ({
|
|
479
566
|
next: [],
|
|
@@ -544,6 +631,27 @@ function KeepProviderContent({
|
|
|
544
631
|
validateItem
|
|
545
632
|
]
|
|
546
633
|
);
|
|
634
|
+
useEffect(() => {
|
|
635
|
+
const settings = autoRevalidationRef.current;
|
|
636
|
+
const activeRevalidator = settings?.revalidator ?? validateItem;
|
|
637
|
+
if (!settings || !activeRevalidator) return;
|
|
638
|
+
const run = () => void revalidateItems(activeRevalidator, { removeStatuses: settings.removeStatuses }).catch(() => void 0);
|
|
639
|
+
if (isHydrated && settings.onMount !== false && !didMountRevalidateRef.current) {
|
|
640
|
+
didMountRevalidateRef.current = true;
|
|
641
|
+
run();
|
|
642
|
+
}
|
|
643
|
+
const interval = settings.intervalMs && settings.intervalMs > 0 ? setInterval(run, settings.intervalMs) : void 0;
|
|
644
|
+
const onOnline = () => {
|
|
645
|
+
if (settings.onReconnect !== false) run();
|
|
646
|
+
};
|
|
647
|
+
if (typeof window !== "undefined" && settings.onReconnect !== false) {
|
|
648
|
+
window.addEventListener("online", onOnline);
|
|
649
|
+
}
|
|
650
|
+
return () => {
|
|
651
|
+
if (interval) clearInterval(interval);
|
|
652
|
+
if (typeof window !== "undefined") window.removeEventListener("online", onOnline);
|
|
653
|
+
};
|
|
654
|
+
}, [isHydrated, revalidateItems, validateItem]);
|
|
547
655
|
const refreshItemMetadata = useCallback(
|
|
548
656
|
async (id, refresh2) => {
|
|
549
657
|
if (!itemsRef.current.some((item) => item.id === id)) {
|
|
@@ -557,6 +665,15 @@ function KeepProviderContent({
|
|
|
557
665
|
[revalidateItems]
|
|
558
666
|
);
|
|
559
667
|
const flushSync = useCallback(() => syncStorage ? syncStorage.flushSync() : Promise.resolve(), [syncStorage]);
|
|
668
|
+
const resolveSyncConflict = useCallback(
|
|
669
|
+
(id, resolution, item) => {
|
|
670
|
+
if (!syncStorage?.resolveSyncConflict) {
|
|
671
|
+
return Promise.reject(new Error("The configured storage does not support sync conflict resolution."));
|
|
672
|
+
}
|
|
673
|
+
return syncStorage.resolveSyncConflict(id, resolution, item);
|
|
674
|
+
},
|
|
675
|
+
[syncStorage]
|
|
676
|
+
);
|
|
560
677
|
const exportBackup = useCallback(() => exportItems(storage), [storage]);
|
|
561
678
|
const importBackup = useCallback(
|
|
562
679
|
async (data, options = {}) => {
|
|
@@ -594,6 +711,7 @@ function KeepProviderContent({
|
|
|
594
711
|
error,
|
|
595
712
|
lastChange,
|
|
596
713
|
syncState,
|
|
714
|
+
undo,
|
|
597
715
|
saveItem,
|
|
598
716
|
updateNote,
|
|
599
717
|
updateTags,
|
|
@@ -602,9 +720,13 @@ function KeepProviderContent({
|
|
|
602
720
|
removeTagsBatch,
|
|
603
721
|
removeItem,
|
|
604
722
|
removeItems,
|
|
723
|
+
removeItemWithUndo,
|
|
724
|
+
removeItemsWithUndo,
|
|
725
|
+
undoLastRemoval,
|
|
605
726
|
clear,
|
|
606
727
|
refresh,
|
|
607
728
|
flushSync,
|
|
729
|
+
resolveSyncConflict,
|
|
608
730
|
refreshItemMetadata,
|
|
609
731
|
revalidateItems,
|
|
610
732
|
exportBackup,
|
|
@@ -615,14 +737,19 @@ function KeepProviderContent({
|
|
|
615
737
|
error,
|
|
616
738
|
lastChange,
|
|
617
739
|
flushSync,
|
|
740
|
+
resolveSyncConflict,
|
|
618
741
|
isHydrated,
|
|
619
742
|
isLoading,
|
|
620
743
|
isMutating,
|
|
621
744
|
items,
|
|
622
745
|
syncState,
|
|
746
|
+
undo,
|
|
623
747
|
refresh,
|
|
624
748
|
removeItem,
|
|
625
749
|
saveItem,
|
|
750
|
+
removeItemWithUndo,
|
|
751
|
+
removeItemsWithUndo,
|
|
752
|
+
undoLastRemoval,
|
|
626
753
|
updateNote,
|
|
627
754
|
updateTags,
|
|
628
755
|
updateTagsBatch,
|
|
@@ -645,6 +772,9 @@ function KeepProviderContent({
|
|
|
645
772
|
removeTagsBatch,
|
|
646
773
|
removeItem,
|
|
647
774
|
removeItems,
|
|
775
|
+
removeItemWithUndo,
|
|
776
|
+
removeItemsWithUndo,
|
|
777
|
+
undoLastRemoval,
|
|
648
778
|
clear,
|
|
649
779
|
refresh,
|
|
650
780
|
refreshItemMetadata,
|
|
@@ -662,7 +792,10 @@ function KeepProviderContent({
|
|
|
662
792
|
updateTags,
|
|
663
793
|
updateTagsBatch,
|
|
664
794
|
refreshItemMetadata,
|
|
665
|
-
revalidateItems
|
|
795
|
+
revalidateItems,
|
|
796
|
+
removeItemWithUndo,
|
|
797
|
+
removeItemsWithUndo,
|
|
798
|
+
undoLastRemoval
|
|
666
799
|
]
|
|
667
800
|
);
|
|
668
801
|
const storeAccess = useMemo(() => ({ store, actions }), [actions, store]);
|
|
@@ -671,11 +804,16 @@ function KeepProviderContent({
|
|
|
671
804
|
var IDLE_SYNC_STATE = Object.freeze({
|
|
672
805
|
status: "idle",
|
|
673
806
|
pendingCount: 0,
|
|
674
|
-
conflictIds: []
|
|
807
|
+
conflictIds: [],
|
|
808
|
+
conflicts: []
|
|
675
809
|
});
|
|
810
|
+
var EMPTY_UNDO_STATE = Object.freeze({ canUndo: false, ids: [] });
|
|
676
811
|
function isSyncCapableStorage(storage) {
|
|
677
812
|
return "getSyncState" in storage && typeof storage.getSyncState === "function" && "subscribeSync" in storage && typeof storage.subscribeSync === "function" && "flushSync" in storage && typeof storage.flushSync === "function";
|
|
678
813
|
}
|
|
814
|
+
function isScopeAwareStorage(storage) {
|
|
815
|
+
return "subscribeScope" in storage && typeof storage.subscribeScope === "function";
|
|
816
|
+
}
|
|
679
817
|
async function parseKeepMetaItem(item, schema) {
|
|
680
818
|
return { ...item, meta: await parseKeepMeta(schema, item.meta) };
|
|
681
819
|
}
|
|
@@ -745,6 +883,7 @@ function useKeepItem(input) {
|
|
|
745
883
|
});
|
|
746
884
|
}, [actions, input, item?.savedAt]);
|
|
747
885
|
const remove = useCallback3(() => actions.removeItem(id), [actions, id]);
|
|
886
|
+
const removeWithUndo = useCallback3(() => actions.removeItemWithUndo(id), [actions, id]);
|
|
748
887
|
const toggle = useCallback3(() => item ? remove() : save(), [item, remove, save]);
|
|
749
888
|
const updateNote = useCallback3((note) => actions.updateNote(id, note), [actions, id]);
|
|
750
889
|
const updateTags = useCallback3((tags) => actions.updateTags(id, tags), [actions, id]);
|
|
@@ -760,6 +899,8 @@ function useKeepItem(input) {
|
|
|
760
899
|
error,
|
|
761
900
|
save,
|
|
762
901
|
remove,
|
|
902
|
+
removeWithUndo,
|
|
903
|
+
undo: actions.undoLastRemoval,
|
|
763
904
|
toggle,
|
|
764
905
|
updateNote,
|
|
765
906
|
updateTags,
|
|
@@ -814,6 +955,8 @@ function useKeepList(query = {}) {
|
|
|
814
955
|
const allTags = useKeepStoreSelector(store, tagsSelector);
|
|
815
956
|
const remove = useCallback4((id) => actions.removeItem(id), [actions]);
|
|
816
957
|
const removeBatch = useCallback4((ids) => actions.removeItems(ids), [actions]);
|
|
958
|
+
const removeWithUndo = useCallback4((id) => actions.removeItemWithUndo(id), [actions]);
|
|
959
|
+
const removeBatchWithUndo = useCallback4((ids) => actions.removeItemsWithUndo(ids), [actions]);
|
|
817
960
|
const updateTagsBatch = useCallback4(
|
|
818
961
|
(ids, nextTags) => actions.updateTagsBatch(ids, nextTags),
|
|
819
962
|
[actions]
|
|
@@ -841,6 +984,8 @@ function useKeepList(query = {}) {
|
|
|
841
984
|
error,
|
|
842
985
|
remove,
|
|
843
986
|
removeBatch,
|
|
987
|
+
removeWithUndo,
|
|
988
|
+
removeBatchWithUndo,
|
|
844
989
|
updateTagsBatch,
|
|
845
990
|
addTagsBatch,
|
|
846
991
|
removeTagsBatch,
|