@keepkit/core 0.9.0 → 0.11.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-DBRHZ6XU.js → chunk-36YIELZE.js} +192 -25
- package/dist/chunk-36YIELZE.js.map +1 -0
- package/dist/chunk-MDTL5L64.js +320 -0
- package/dist/chunk-MDTL5L64.js.map +1 -0
- package/dist/core.d.ts +27 -48
- package/dist/core.js +110 -131
- package/dist/core.js.map +1 -1
- package/dist/react.d.ts +23 -7
- package/dist/react.js +188 -11
- package/dist/react.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/scope-CtLSlZhq.d.ts +38 -0
- package/dist/storage.d.ts +19 -2
- package/dist/storage.js +7 -1
- package/dist/{types-BCziYE6E.d.ts → types-D-xRiz1Y.d.ts} +22 -2
- package/dist/url-BYFEfulm.d.ts +186 -0
- package/package.json +1 -1
- package/dist/chunk-AWQWKK4V.js +0 -151
- package/dist/chunk-AWQWKK4V.js.map +0 -1
- package/dist/chunk-DBRHZ6XU.js.map +0 -1
- package/dist/store-CqNh-2DS.d.ts +0 -104
package/dist/react.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
KeepStore,
|
|
4
|
+
exportItems,
|
|
5
|
+
importItems,
|
|
4
6
|
isKeepItemMetadataStale,
|
|
5
7
|
queryKeepItems,
|
|
6
8
|
revalidateKeepItems
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MDTL5L64.js";
|
|
8
10
|
import {
|
|
9
11
|
parseKeepMeta
|
|
10
12
|
} from "./chunk-THZ3ACR2.js";
|
|
@@ -12,7 +14,7 @@ import "./chunk-5QSZP6MT.js";
|
|
|
12
14
|
import {
|
|
13
15
|
createBrowserStorageAdapter,
|
|
14
16
|
normalizeKeepTags
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-36YIELZE.js";
|
|
16
18
|
|
|
17
19
|
// src/hooks/useKeepItem.ts
|
|
18
20
|
import { useCallback as useCallback3 } from "react";
|
|
@@ -67,6 +69,7 @@ function KeepProvider({
|
|
|
67
69
|
onNoteUpdate,
|
|
68
70
|
onTagsUpdate,
|
|
69
71
|
onChange,
|
|
72
|
+
onUndo,
|
|
70
73
|
onError,
|
|
71
74
|
plugins = [],
|
|
72
75
|
schemaVersion,
|
|
@@ -77,6 +80,10 @@ function KeepProvider({
|
|
|
77
80
|
fallback,
|
|
78
81
|
onBoundaryError,
|
|
79
82
|
boundaryResetKey,
|
|
83
|
+
validateItem,
|
|
84
|
+
resolveItem,
|
|
85
|
+
autoRevalidation,
|
|
86
|
+
undoTimeoutMs,
|
|
80
87
|
children
|
|
81
88
|
}) {
|
|
82
89
|
const content = /* @__PURE__ */ jsx(
|
|
@@ -89,6 +96,7 @@ function KeepProvider({
|
|
|
89
96
|
onNoteUpdate,
|
|
90
97
|
onTagsUpdate,
|
|
91
98
|
onChange,
|
|
99
|
+
onUndo,
|
|
92
100
|
onError,
|
|
93
101
|
plugins,
|
|
94
102
|
schemaVersion,
|
|
@@ -96,6 +104,10 @@ function KeepProvider({
|
|
|
96
104
|
invalidItemPolicy,
|
|
97
105
|
onInvalidItem,
|
|
98
106
|
migrateMeta,
|
|
107
|
+
validateItem,
|
|
108
|
+
resolveItem,
|
|
109
|
+
autoRevalidation,
|
|
110
|
+
undoTimeoutMs,
|
|
99
111
|
children
|
|
100
112
|
}
|
|
101
113
|
);
|
|
@@ -110,6 +122,7 @@ function KeepProviderContent({
|
|
|
110
122
|
onNoteUpdate,
|
|
111
123
|
onTagsUpdate,
|
|
112
124
|
onChange,
|
|
125
|
+
onUndo,
|
|
113
126
|
onError,
|
|
114
127
|
plugins = [],
|
|
115
128
|
schemaVersion,
|
|
@@ -117,6 +130,10 @@ function KeepProviderContent({
|
|
|
117
130
|
invalidItemPolicy = "error",
|
|
118
131
|
onInvalidItem,
|
|
119
132
|
migrateMeta,
|
|
133
|
+
validateItem,
|
|
134
|
+
resolveItem,
|
|
135
|
+
autoRevalidation,
|
|
136
|
+
undoTimeoutMs = 5e3,
|
|
120
137
|
children
|
|
121
138
|
}) {
|
|
122
139
|
const storeRef = useRef(null);
|
|
@@ -127,17 +144,19 @@ function KeepProviderContent({
|
|
|
127
144
|
isHydrated: false,
|
|
128
145
|
isMutating: false,
|
|
129
146
|
error: null,
|
|
130
|
-
lastChange: void 0
|
|
147
|
+
lastChange: void 0,
|
|
148
|
+
undo: { canUndo: false, ids: [] }
|
|
131
149
|
});
|
|
132
150
|
}
|
|
133
151
|
const store = storeRef.current;
|
|
134
152
|
const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
135
|
-
const { items, isLoading, isHydrated, isMutating, error, lastChange } = state;
|
|
153
|
+
const { items, isLoading, isHydrated, isMutating, error, lastChange, undo: storedUndo } = state;
|
|
154
|
+
const undo = storedUndo ?? EMPTY_UNDO_STATE;
|
|
136
155
|
const itemsRef = useRef(items);
|
|
137
156
|
const pluginsRef = useRef(plugins);
|
|
138
157
|
pluginsRef.current = plugins;
|
|
139
|
-
const handlersRef = useRef({ onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onError });
|
|
140
|
-
handlersRef.current = { onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onError };
|
|
158
|
+
const handlersRef = useRef({ onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onUndo, onError });
|
|
159
|
+
handlersRef.current = { onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onUndo, onError };
|
|
141
160
|
const migrationRef = useRef({
|
|
142
161
|
schemaVersion,
|
|
143
162
|
migrateMeta,
|
|
@@ -149,6 +168,10 @@ function KeepProviderContent({
|
|
|
149
168
|
const operationTailRef = useRef(Promise.resolve());
|
|
150
169
|
const pendingRefreshesRef = useRef(0);
|
|
151
170
|
const pendingMutationsRef = useRef(0);
|
|
171
|
+
const undoRef = useRef(void 0);
|
|
172
|
+
const autoRevalidationRef = useRef(autoRevalidation);
|
|
173
|
+
autoRevalidationRef.current = autoRevalidation;
|
|
174
|
+
const didMountRevalidateRef = useRef(false);
|
|
152
175
|
const syncStorage = isSyncCapableStorage(storage) ? storage : void 0;
|
|
153
176
|
const getSyncState = useCallback(() => syncStorage?.getSyncState() ?? IDLE_SYNC_STATE, [syncStorage]);
|
|
154
177
|
const subscribeSync = useCallback(
|
|
@@ -466,6 +489,69 @@ function KeepProviderContent({
|
|
|
466
489
|
},
|
|
467
490
|
[runMutation, storage]
|
|
468
491
|
);
|
|
492
|
+
const rememberUndo = useCallback(
|
|
493
|
+
(removedItems) => {
|
|
494
|
+
undoRef.current?.timer && clearTimeout(undoRef.current.timer);
|
|
495
|
+
const expiresAt = Date.now() + Math.max(0, undoTimeoutMs);
|
|
496
|
+
const timer = setTimeout(
|
|
497
|
+
() => {
|
|
498
|
+
undoRef.current = void 0;
|
|
499
|
+
store.setState({ undo: { canUndo: false, ids: [] } });
|
|
500
|
+
},
|
|
501
|
+
Math.max(0, undoTimeoutMs)
|
|
502
|
+
);
|
|
503
|
+
undoRef.current = { items: removedItems, expiresAt, timer };
|
|
504
|
+
store.setState({ undo: { canUndo: true, ids: removedItems.map((item) => item.id), expiresAt } });
|
|
505
|
+
},
|
|
506
|
+
[store, undoTimeoutMs]
|
|
507
|
+
);
|
|
508
|
+
const removeItemWithUndo = useCallback(
|
|
509
|
+
async (id) => {
|
|
510
|
+
const item = itemsRef.current.find((current) => current.id === id);
|
|
511
|
+
await removeItem(id);
|
|
512
|
+
if (item) rememberUndo([item]);
|
|
513
|
+
},
|
|
514
|
+
[rememberUndo, removeItem]
|
|
515
|
+
);
|
|
516
|
+
const removeItemsWithUndo = useCallback(
|
|
517
|
+
async (ids) => {
|
|
518
|
+
const idSet = new Set(ids);
|
|
519
|
+
const removedItems = itemsRef.current.filter((item) => idSet.has(item.id));
|
|
520
|
+
await removeItems(ids);
|
|
521
|
+
if (removedItems.length > 0) rememberUndo(removedItems);
|
|
522
|
+
},
|
|
523
|
+
[rememberUndo, removeItems]
|
|
524
|
+
);
|
|
525
|
+
const undoLastRemoval = useCallback(async () => {
|
|
526
|
+
const pending = undoRef.current;
|
|
527
|
+
if (!pending || pending.expiresAt < Date.now()) {
|
|
528
|
+
undoRef.current = void 0;
|
|
529
|
+
store.setState({ undo: { canUndo: false, ids: [] } });
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (pending.timer) clearTimeout(pending.timer);
|
|
533
|
+
undoRef.current = void 0;
|
|
534
|
+
store.setState({ undo: { canUndo: false, ids: [] } });
|
|
535
|
+
await runMutation("undo", void 0, (previous) => {
|
|
536
|
+
const restored = new Map(pending.items.map((item) => [item.id, item]));
|
|
537
|
+
const next = [...previous.filter((item) => !restored.has(item.id)), ...pending.items].sort(
|
|
538
|
+
(a, b) => b.updatedAt - a.updatedAt
|
|
539
|
+
);
|
|
540
|
+
return {
|
|
541
|
+
next,
|
|
542
|
+
persist: async () => {
|
|
543
|
+
if (storage.setMany) await storage.setMany(pending.items);
|
|
544
|
+
else for (const item of pending.items) await storage.set(item);
|
|
545
|
+
},
|
|
546
|
+
onSuccess: () => handlersRef.current.onUndo?.(pending.items),
|
|
547
|
+
pluginContext: { action: "undo", items: pending.items }
|
|
548
|
+
};
|
|
549
|
+
});
|
|
550
|
+
}, [runMutation, storage, store]);
|
|
551
|
+
useEffect(() => {
|
|
552
|
+
if (syncState.status !== "error" || !undoRef.current) return;
|
|
553
|
+
void undoLastRemoval();
|
|
554
|
+
}, [syncState.status, undoLastRemoval]);
|
|
469
555
|
const clear = useCallback(
|
|
470
556
|
() => runMutation("clear", void 0, (_previous) => ({
|
|
471
557
|
next: [],
|
|
@@ -482,7 +568,13 @@ function KeepProviderContent({
|
|
|
482
568
|
const previous = itemsRef.current;
|
|
483
569
|
let persistenceStarted = false;
|
|
484
570
|
try {
|
|
485
|
-
const
|
|
571
|
+
const activeRevalidator = revalidator ?? validateItem;
|
|
572
|
+
if (!activeRevalidator)
|
|
573
|
+
throw new Error("KeepProvider.revalidateItems requires a revalidator or validateItem.");
|
|
574
|
+
const summary = await revalidateKeepItems(previous, activeRevalidator, {
|
|
575
|
+
...options,
|
|
576
|
+
resolveItem: options.resolveItem ?? resolveItem
|
|
577
|
+
});
|
|
486
578
|
const pluginContext = { action: "revalidate", items: summary.updatedItems };
|
|
487
579
|
await runBeforePlugins(pluginContext);
|
|
488
580
|
if (summary.updatedItems.length > 0) {
|
|
@@ -518,8 +610,39 @@ function KeepProviderContent({
|
|
|
518
610
|
if (pendingMutationsRef.current === 0) store.setState({ isMutating: false });
|
|
519
611
|
});
|
|
520
612
|
},
|
|
521
|
-
[
|
|
613
|
+
[
|
|
614
|
+
enqueueOperation,
|
|
615
|
+
reportError,
|
|
616
|
+
resolveItem,
|
|
617
|
+
runAfterPlugins,
|
|
618
|
+
runBeforePlugins,
|
|
619
|
+
setItems,
|
|
620
|
+
storage,
|
|
621
|
+
store,
|
|
622
|
+
validateItem
|
|
623
|
+
]
|
|
522
624
|
);
|
|
625
|
+
useEffect(() => {
|
|
626
|
+
const settings = autoRevalidationRef.current;
|
|
627
|
+
const activeRevalidator = settings?.revalidator ?? validateItem;
|
|
628
|
+
if (!settings || !activeRevalidator) return;
|
|
629
|
+
const run = () => void revalidateItems(activeRevalidator, { removeStatuses: settings.removeStatuses }).catch(() => void 0);
|
|
630
|
+
if (isHydrated && settings.onMount !== false && !didMountRevalidateRef.current) {
|
|
631
|
+
didMountRevalidateRef.current = true;
|
|
632
|
+
run();
|
|
633
|
+
}
|
|
634
|
+
const interval = settings.intervalMs && settings.intervalMs > 0 ? setInterval(run, settings.intervalMs) : void 0;
|
|
635
|
+
const onOnline = () => {
|
|
636
|
+
if (settings.onReconnect !== false) run();
|
|
637
|
+
};
|
|
638
|
+
if (typeof window !== "undefined" && settings.onReconnect !== false) {
|
|
639
|
+
window.addEventListener("online", onOnline);
|
|
640
|
+
}
|
|
641
|
+
return () => {
|
|
642
|
+
if (interval) clearInterval(interval);
|
|
643
|
+
if (typeof window !== "undefined") window.removeEventListener("online", onOnline);
|
|
644
|
+
};
|
|
645
|
+
}, [isHydrated, revalidateItems, validateItem]);
|
|
523
646
|
const refreshItemMetadata = useCallback(
|
|
524
647
|
async (id, refresh2) => {
|
|
525
648
|
if (!itemsRef.current.some((item) => item.id === id)) {
|
|
@@ -533,6 +656,34 @@ function KeepProviderContent({
|
|
|
533
656
|
[revalidateItems]
|
|
534
657
|
);
|
|
535
658
|
const flushSync = useCallback(() => syncStorage ? syncStorage.flushSync() : Promise.resolve(), [syncStorage]);
|
|
659
|
+
const exportBackup = useCallback(() => exportItems(storage), [storage]);
|
|
660
|
+
const importBackup = useCallback(
|
|
661
|
+
async (data, options = {}) => {
|
|
662
|
+
pendingMutationsRef.current += 1;
|
|
663
|
+
store.setState({ isMutating: true });
|
|
664
|
+
const run = enqueueOperation(async () => {
|
|
665
|
+
try {
|
|
666
|
+
const result = await importItems(storage, data, {
|
|
667
|
+
...options,
|
|
668
|
+
schema: migrationRef.current.schema,
|
|
669
|
+
invalidItemPolicy: options.invalidItemPolicy ?? migrationRef.current.invalidItemPolicy,
|
|
670
|
+
onInvalidItem: options.onInvalidItem ?? migrationRef.current.onInvalidItem
|
|
671
|
+
});
|
|
672
|
+
setItems(result.items);
|
|
673
|
+
store.setState({ error: null });
|
|
674
|
+
return result;
|
|
675
|
+
} catch (cause) {
|
|
676
|
+
reportError(cause, { action: "import" });
|
|
677
|
+
throw cause;
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
return run.finally(() => {
|
|
681
|
+
pendingMutationsRef.current -= 1;
|
|
682
|
+
if (pendingMutationsRef.current === 0) store.setState({ isMutating: false });
|
|
683
|
+
});
|
|
684
|
+
},
|
|
685
|
+
[enqueueOperation, reportError, setItems, storage, store]
|
|
686
|
+
);
|
|
536
687
|
const value = useMemo(
|
|
537
688
|
() => ({
|
|
538
689
|
items,
|
|
@@ -542,6 +693,7 @@ function KeepProviderContent({
|
|
|
542
693
|
error,
|
|
543
694
|
lastChange,
|
|
544
695
|
syncState,
|
|
696
|
+
undo,
|
|
545
697
|
saveItem,
|
|
546
698
|
updateNote,
|
|
547
699
|
updateTags,
|
|
@@ -550,11 +702,16 @@ function KeepProviderContent({
|
|
|
550
702
|
removeTagsBatch,
|
|
551
703
|
removeItem,
|
|
552
704
|
removeItems,
|
|
705
|
+
removeItemWithUndo,
|
|
706
|
+
removeItemsWithUndo,
|
|
707
|
+
undoLastRemoval,
|
|
553
708
|
clear,
|
|
554
709
|
refresh,
|
|
555
710
|
flushSync,
|
|
556
711
|
refreshItemMetadata,
|
|
557
|
-
revalidateItems
|
|
712
|
+
revalidateItems,
|
|
713
|
+
exportBackup,
|
|
714
|
+
importBackup
|
|
558
715
|
}),
|
|
559
716
|
[
|
|
560
717
|
clear,
|
|
@@ -566,9 +723,13 @@ function KeepProviderContent({
|
|
|
566
723
|
isMutating,
|
|
567
724
|
items,
|
|
568
725
|
syncState,
|
|
726
|
+
undo,
|
|
569
727
|
refresh,
|
|
570
728
|
removeItem,
|
|
571
729
|
saveItem,
|
|
730
|
+
removeItemWithUndo,
|
|
731
|
+
removeItemsWithUndo,
|
|
732
|
+
undoLastRemoval,
|
|
572
733
|
updateNote,
|
|
573
734
|
updateTags,
|
|
574
735
|
updateTagsBatch,
|
|
@@ -576,7 +737,9 @@ function KeepProviderContent({
|
|
|
576
737
|
removeTagsBatch,
|
|
577
738
|
removeItems,
|
|
578
739
|
refreshItemMetadata,
|
|
579
|
-
revalidateItems
|
|
740
|
+
revalidateItems,
|
|
741
|
+
exportBackup,
|
|
742
|
+
importBackup
|
|
580
743
|
]
|
|
581
744
|
);
|
|
582
745
|
const actions = useMemo(
|
|
@@ -589,6 +752,9 @@ function KeepProviderContent({
|
|
|
589
752
|
removeTagsBatch,
|
|
590
753
|
removeItem,
|
|
591
754
|
removeItems,
|
|
755
|
+
removeItemWithUndo,
|
|
756
|
+
removeItemsWithUndo,
|
|
757
|
+
undoLastRemoval,
|
|
592
758
|
clear,
|
|
593
759
|
refresh,
|
|
594
760
|
refreshItemMetadata,
|
|
@@ -606,7 +772,10 @@ function KeepProviderContent({
|
|
|
606
772
|
updateTags,
|
|
607
773
|
updateTagsBatch,
|
|
608
774
|
refreshItemMetadata,
|
|
609
|
-
revalidateItems
|
|
775
|
+
revalidateItems,
|
|
776
|
+
removeItemWithUndo,
|
|
777
|
+
removeItemsWithUndo,
|
|
778
|
+
undoLastRemoval
|
|
610
779
|
]
|
|
611
780
|
);
|
|
612
781
|
const storeAccess = useMemo(() => ({ store, actions }), [actions, store]);
|
|
@@ -617,6 +786,7 @@ var IDLE_SYNC_STATE = Object.freeze({
|
|
|
617
786
|
pendingCount: 0,
|
|
618
787
|
conflictIds: []
|
|
619
788
|
});
|
|
789
|
+
var EMPTY_UNDO_STATE = Object.freeze({ canUndo: false, ids: [] });
|
|
620
790
|
function isSyncCapableStorage(storage) {
|
|
621
791
|
return "getSyncState" in storage && typeof storage.getSyncState === "function" && "subscribeSync" in storage && typeof storage.subscribeSync === "function" && "flushSync" in storage && typeof storage.flushSync === "function";
|
|
622
792
|
}
|
|
@@ -689,6 +859,7 @@ function useKeepItem(input) {
|
|
|
689
859
|
});
|
|
690
860
|
}, [actions, input, item?.savedAt]);
|
|
691
861
|
const remove = useCallback3(() => actions.removeItem(id), [actions, id]);
|
|
862
|
+
const removeWithUndo = useCallback3(() => actions.removeItemWithUndo(id), [actions, id]);
|
|
692
863
|
const toggle = useCallback3(() => item ? remove() : save(), [item, remove, save]);
|
|
693
864
|
const updateNote = useCallback3((note) => actions.updateNote(id, note), [actions, id]);
|
|
694
865
|
const updateTags = useCallback3((tags) => actions.updateTags(id, tags), [actions, id]);
|
|
@@ -704,6 +875,8 @@ function useKeepItem(input) {
|
|
|
704
875
|
error,
|
|
705
876
|
save,
|
|
706
877
|
remove,
|
|
878
|
+
removeWithUndo,
|
|
879
|
+
undo: actions.undoLastRemoval,
|
|
707
880
|
toggle,
|
|
708
881
|
updateNote,
|
|
709
882
|
updateTags,
|
|
@@ -758,6 +931,8 @@ function useKeepList(query = {}) {
|
|
|
758
931
|
const allTags = useKeepStoreSelector(store, tagsSelector);
|
|
759
932
|
const remove = useCallback4((id) => actions.removeItem(id), [actions]);
|
|
760
933
|
const removeBatch = useCallback4((ids) => actions.removeItems(ids), [actions]);
|
|
934
|
+
const removeWithUndo = useCallback4((id) => actions.removeItemWithUndo(id), [actions]);
|
|
935
|
+
const removeBatchWithUndo = useCallback4((ids) => actions.removeItemsWithUndo(ids), [actions]);
|
|
761
936
|
const updateTagsBatch = useCallback4(
|
|
762
937
|
(ids, nextTags) => actions.updateTagsBatch(ids, nextTags),
|
|
763
938
|
[actions]
|
|
@@ -785,6 +960,8 @@ function useKeepList(query = {}) {
|
|
|
785
960
|
error,
|
|
786
961
|
remove,
|
|
787
962
|
removeBatch,
|
|
963
|
+
removeWithUndo,
|
|
964
|
+
removeBatchWithUndo,
|
|
788
965
|
updateTagsBatch,
|
|
789
966
|
addTagsBatch,
|
|
790
967
|
removeTagsBatch,
|