@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/dist/core.js CHANGED
@@ -1,11 +1,19 @@
1
1
  import {
2
+ KEEP_BACKUP_FORMAT,
3
+ KEEP_BACKUP_VERSION,
4
+ KeepBackupImportError,
5
+ KeepBackupParseError,
2
6
  KeepStore,
7
+ exportItems,
3
8
  getTagCounts,
9
+ importItems,
4
10
  isKeepItemMetadataStale,
11
+ mergeKeepItems,
12
+ migrateKeepItems,
5
13
  queryKeepItems,
6
14
  reconcileKeepItems,
7
15
  revalidateKeepItems
8
- } from "./chunk-AWQWKK4V.js";
16
+ } from "./chunk-MDTL5L64.js";
9
17
  import {
10
18
  KeepSchemaValidationError,
11
19
  parseKeepMeta,
@@ -28,158 +36,118 @@ import {
28
36
  KeepStorageQuotaError,
29
37
  LocalStorageAdapter,
30
38
  LocalStorageSyncQueueAdapter,
39
+ ScopedStorageAdapter,
40
+ ScopedSyncQueueAdapter,
31
41
  SyncStorageAdapter,
32
42
  createBrowserStorageAdapter,
43
+ createScopedStorageAdapter,
33
44
  createStorageAdapter,
45
+ getKeepScopeKey,
46
+ isSameKeepScope,
34
47
  normalizeKeepTags
35
- } from "./chunk-DBRHZ6XU.js";
48
+ } from "./chunk-36YIELZE.js";
36
49
 
37
- // src/migration.ts
38
- async function mergeKeepItems(localItems, target) {
39
- if (target.merge) return target.merge(localItems);
40
- const remoteItems = await target.getAll();
41
- const byId = new Map(remoteItems.map((item) => [item.id, item]));
42
- for (const localItem of localItems) {
43
- const remoteItem = byId.get(localItem.id);
44
- if (!remoteItem || localItem.updatedAt > remoteItem.updatedAt) {
45
- byId.set(localItem.id, localItem);
50
+ // src/integrations.ts
51
+ function createKeepInvalidationPlugin(options) {
52
+ return {
53
+ name: options.name ?? "keepkit-cache-invalidation",
54
+ after: async (context) => {
55
+ const keys = typeof options.queryKeys === "function" ? options.queryKeys(context) : [options.queryKeys];
56
+ await Promise.all(keys.map((queryKey) => options.invalidate(queryKey, context)));
46
57
  }
47
- }
48
- const merged = [...byId.values()].sort((a, b) => b.updatedAt - a.updatedAt);
49
- await Promise.all(merged.map((item) => target.set(item)));
50
- return merged;
51
- }
52
- async function migrateKeepItems(source, target) {
53
- const localItems = await source.getAll();
54
- const merged = await mergeKeepItems(localItems, target);
55
- await source.clear();
56
- return merged;
58
+ };
57
59
  }
58
60
 
59
- // src/backup.ts
60
- var KEEP_BACKUP_FORMAT = "keepkit";
61
- var KEEP_BACKUP_VERSION = 1;
62
- var KeepBackupParseError = class extends Error {
63
- constructor(message, options) {
64
- super(message);
65
- this.name = "KeepBackupParseError";
66
- if (options?.cause !== void 0) this.cause = options.cause;
61
+ // src/presets.ts
62
+ function createKeepKitPreset(options = {}) {
63
+ const mode = options.mode ?? "local";
64
+ const local = options.storage ? options.scope ? createScopedStorageAdapter(options.storage, options.scope) : options.storage : createBrowserStorageAdapter({ key: options.key, scope: options.scope });
65
+ if (mode === "sync" && !options.remote) {
66
+ throw new Error('createKeepKitPreset({ mode: "sync" }) requires a remote driver.');
67
67
  }
68
- };
69
- var KeepBackupImportError = class extends Error {
70
- constructor(message, options) {
71
- super(message);
72
- this.name = "KeepBackupImportError";
73
- this.mode = options.mode;
74
- this.imported = options.imported;
75
- this.failed = options.failed;
76
- if (options.cause !== void 0) this.cause = options.cause;
68
+ let storage = local;
69
+ if (mode === "sync") {
70
+ const remote = options.remote;
71
+ if (!remote) throw new Error('createKeepKitPreset({ mode: "sync" }) requires a remote driver.');
72
+ storage = new SyncStorageAdapter({
73
+ local,
74
+ remote,
75
+ userId: options.scope?.userId,
76
+ tenantId: options.scope?.tenantId
77
+ });
77
78
  }
78
- };
79
- async function exportItems(adapter) {
80
- const backup = {
81
- format: KEEP_BACKUP_FORMAT,
82
- version: KEEP_BACKUP_VERSION,
83
- exportedAt: Date.now(),
84
- items: await adapter.getAll()
79
+ return {
80
+ mode,
81
+ scope: options.scope,
82
+ storage,
83
+ exportBackup: () => exportItems(storage)
85
84
  };
86
- return JSON.stringify(backup, null, 2);
87
- }
88
- async function importItems(adapter, data, options = {}) {
89
- const backup = parseBackup(data);
90
- const mode = options.mode ?? "merge";
91
- const validItems = [];
92
- let failed = 0;
93
- for (const item of backup.items) {
94
- if (!options.schema) {
95
- validItems.push(item);
96
- continue;
97
- }
98
- try {
99
- validItems.push(await validateKeepItem(item, options.schema));
100
- } catch (cause) {
101
- options.onInvalidItem?.(cause, item);
102
- if ((options.invalidItemPolicy ?? "error") === "drop") {
103
- failed += 1;
104
- continue;
105
- }
106
- throw cause;
107
- }
108
- }
109
- let items;
110
- if (mode === "merge") {
111
- try {
112
- items = await mergeKeepItems(validItems, adapter);
113
- } catch (cause) {
114
- throw new KeepBackupImportError("KeepKit could not merge the backup.", {
115
- mode,
116
- imported: 0,
117
- failed: validItems.length + failed,
118
- cause
119
- });
120
- }
121
- } else {
122
- let imported = 0;
123
- try {
124
- await adapter.clear();
125
- for (const item of validItems) {
126
- await adapter.set(item);
127
- imported += 1;
128
- }
129
- items = await adapter.getAll();
130
- } catch (cause) {
131
- throw new KeepBackupImportError("KeepKit could not replace the stored items.", {
132
- mode,
133
- imported,
134
- failed: validItems.length + failed - imported,
135
- cause
136
- });
137
- }
138
- }
139
- return { mode, imported: validItems.length, failed, total: items.length, items };
140
85
  }
141
- function parseBackup(data) {
142
- let value = data;
143
- if (typeof data === "string") {
144
- try {
145
- value = JSON.parse(data);
146
- } catch (cause) {
147
- throw new KeepBackupParseError("KeepKit backup is not valid JSON.", { cause });
148
- }
149
- }
150
- if (!isRecord(value)) throw new KeepBackupParseError("KeepKit backup must be an object.");
151
- if (value.format !== KEEP_BACKUP_FORMAT || value.version !== KEEP_BACKUP_VERSION) {
152
- throw new KeepBackupParseError("KeepKit backup format or version is unsupported.");
153
- }
154
- if (typeof value.exportedAt !== "number" || !Number.isFinite(value.exportedAt)) {
155
- throw new KeepBackupParseError("KeepKit backup has an invalid export timestamp.");
156
- }
157
- if (!Array.isArray(value.items) || !value.items.every(isKeepItem)) {
158
- throw new KeepBackupParseError("KeepKit backup contains invalid items.");
86
+ var createKeepKitSetup = createKeepKitPreset;
87
+
88
+ // src/url.ts
89
+ var DEFAULT_KEEP_URL_PARAMS = {
90
+ search: "q",
91
+ tags: "tag",
92
+ sort: "sort",
93
+ page: "page"
94
+ };
95
+ function encodeKeepListQuery(query = {}, options = {}) {
96
+ const params = { ...DEFAULT_KEEP_URL_PARAMS, ...options.params };
97
+ const result = new URLSearchParams();
98
+ const search = query.search?.query?.trim();
99
+ if (search) result.set(params.search, search);
100
+ for (const tag of query.tags ?? []) {
101
+ const normalized = tag.trim();
102
+ if (normalized) result.append(params.tags, normalized);
159
103
  }
160
- return value;
104
+ if (query.sort?.by) result.set(params.sort, `${query.sort.by}:${query.sort.direction ?? "desc"}`);
105
+ const page = query.pagination?.page;
106
+ if (page !== void 0 && Number.isFinite(page) && page > 1) result.set(params.page, String(Math.floor(page)));
107
+ return result;
161
108
  }
162
- function isKeepItem(value) {
163
- if (!isRecord(value)) return false;
164
- return typeof value.id === "string" && typeof value.savedAt === "number" && Number.isFinite(value.savedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && "meta" in value && (value.targetType === void 0 || typeof value.targetType === "string") && (value.note === void 0 || typeof value.note === "string") && (value.schemaVersion === void 0 || typeof value.schemaVersion === "number" && Number.isFinite(value.schemaVersion)) && (value.revision === void 0 || typeof value.revision === "string") && (value.metaUpdatedAt === void 0 || typeof value.metaUpdatedAt === "number" && Number.isFinite(value.metaUpdatedAt)) && (value.tags === void 0 || Array.isArray(value.tags) && value.tags.every((tag) => typeof tag === "string"));
109
+ function decodeKeepListQuery(input, options = {}) {
110
+ const params = { ...DEFAULT_KEEP_URL_PARAMS, ...options.params };
111
+ const searchParams = input instanceof URLSearchParams ? input : new URL(input, "http://keepkit.invalid").searchParams;
112
+ const search = searchParams.get(params.search)?.trim();
113
+ const tags = [
114
+ ...new Set(
115
+ searchParams.getAll(params.tags).map((tag) => tag.trim()).filter(Boolean)
116
+ )
117
+ ];
118
+ const sortValue = searchParams.get(params.sort)?.split(":");
119
+ const sort = sortValue?.[0] === "savedAt" || sortValue?.[0] === "updatedAt" ? {
120
+ by: sortValue[0],
121
+ direction: sortValue[1] === "asc" ? "asc" : "desc"
122
+ } : void 0;
123
+ const rawPage = Number(searchParams.get(params.page));
124
+ const page = Number.isInteger(rawPage) && rawPage > 0 ? rawPage : void 0;
125
+ return {
126
+ ...search ? { search: { query: search } } : {},
127
+ ...tags.length > 0 ? { tags } : {},
128
+ ...sort ? { sort } : {},
129
+ ...page ? { pagination: { page } } : {}
130
+ };
165
131
  }
166
- function isRecord(value) {
167
- return typeof value === "object" && value !== null;
132
+ function serializeKeepListQuery(query = {}, options = {}) {
133
+ const value = encodeKeepListQuery(query, options).toString();
134
+ return value ? `?${value}` : "";
168
135
  }
169
-
170
- // src/integrations.ts
171
- function createKeepInvalidationPlugin(options) {
136
+ function mergeKeepListQueryFromUrl(query, input, options = {}) {
137
+ const decoded = decodeKeepListQuery(input, options);
172
138
  return {
173
- name: options.name ?? "keepkit-cache-invalidation",
174
- after: async (context) => {
175
- const keys = typeof options.queryKeys === "function" ? options.queryKeys(context) : [options.queryKeys];
176
- await Promise.all(keys.map((queryKey) => options.invalidate(queryKey, context)));
177
- }
139
+ ...query,
140
+ ...decoded,
141
+ search: decoded.search ?? query.search,
142
+ tags: decoded.tags ?? query.tags,
143
+ sort: decoded.sort ?? query.sort,
144
+ pagination: decoded.pagination ? { ...query.pagination, ...decoded.pagination } : query.pagination
178
145
  };
179
146
  }
180
147
  export {
181
148
  DEFAULT_INDEXEDDB_DATABASE,
182
149
  DEFAULT_INDEXEDDB_STORE,
150
+ DEFAULT_KEEP_URL_PARAMS,
183
151
  DEFAULT_STORAGE_KEY,
184
152
  DEFAULT_SYNC_QUEUE_DATABASE,
185
153
  DEFAULT_SYNC_QUEUE_KEY,
@@ -200,21 +168,32 @@ export {
200
168
  KeepStore,
201
169
  LocalStorageAdapter,
202
170
  LocalStorageSyncQueueAdapter,
171
+ ScopedStorageAdapter,
172
+ ScopedSyncQueueAdapter,
203
173
  SyncStorageAdapter,
204
174
  createBrowserStorageAdapter,
205
175
  createKeepInvalidationPlugin,
176
+ createKeepKitPreset,
177
+ createKeepKitSetup,
178
+ createScopedStorageAdapter,
206
179
  createStorageAdapter,
180
+ decodeKeepListQuery,
181
+ encodeKeepListQuery,
207
182
  exportItems,
183
+ getKeepScopeKey,
208
184
  getTagCounts,
209
185
  importItems,
210
186
  isKeepItemMetadataStale,
187
+ isSameKeepScope,
211
188
  mergeKeepItems,
189
+ mergeKeepListQueryFromUrl,
212
190
  migrateKeepItems,
213
191
  normalizeKeepTags,
214
192
  parseKeepMeta,
215
193
  queryKeepItems,
216
194
  reconcileKeepItems,
217
195
  revalidateKeepItems,
196
+ serializeKeepListQuery,
218
197
  validateKeepItem
219
198
  };
220
199
  //# sourceMappingURL=core.js.map
package/dist/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/migration.ts","../src/backup.ts","../src/integrations.ts"],"sourcesContent":["import type { KeepItem, StorageAdapter } from \"./types\";\n\n/** Merge anonymous local items into a signed-in or remote adapter. */\nexport async function mergeKeepItems<TMeta>(\n localItems: KeepItem<TMeta>[],\n target: StorageAdapter<TMeta>,\n): Promise<KeepItem<TMeta>[]> {\n if (target.merge) return target.merge(localItems);\n\n const remoteItems = await target.getAll();\n const byId = new Map(remoteItems.map((item) => [item.id, item]));\n for (const localItem of localItems) {\n const remoteItem = byId.get(localItem.id);\n if (!remoteItem || localItem.updatedAt > remoteItem.updatedAt) {\n byId.set(localItem.id, localItem);\n }\n }\n\n const merged = [...byId.values()].sort((a, b) => b.updatedAt - a.updatedAt);\n await Promise.all(merged.map((item) => target.set(item)));\n return merged;\n}\n\n/** Read anonymous items, merge them into the target, then clear the source. */\nexport async function migrateKeepItems<TMeta>(\n source: StorageAdapter<TMeta>,\n target: StorageAdapter<TMeta>,\n): Promise<KeepItem<TMeta>[]> {\n const localItems = await source.getAll();\n const merged = await mergeKeepItems(localItems, target);\n await source.clear();\n return merged;\n}\n","import { mergeKeepItems } from \"./migration\";\nimport { validateKeepItem } from \"./schema\";\nimport type { KeepInvalidItemPolicy, KeepItem, KeepSchema, StorageAdapter } from \"./types\";\n\nexport const KEEP_BACKUP_FORMAT = \"keepkit\";\nexport const KEEP_BACKUP_VERSION = 1;\n\nexport type KeepBackup<TMeta = Record<string, unknown>> = {\n format: typeof KEEP_BACKUP_FORMAT;\n version: typeof KEEP_BACKUP_VERSION;\n exportedAt: number;\n items: KeepItem<TMeta>[];\n};\n\nexport type ImportItemsOptions<TMeta = unknown> = {\n mode?: \"replace\" | \"merge\";\n schema?: KeepSchema<TMeta>;\n invalidItemPolicy?: KeepInvalidItemPolicy;\n onInvalidItem?: (error: unknown, item: KeepItem<unknown>) => void;\n};\n\nexport type ImportItemsResult<TMeta = Record<string, unknown>> = {\n mode: \"replace\" | \"merge\";\n imported: number;\n failed: number;\n total: number;\n items: KeepItem<TMeta>[];\n};\n\nexport class KeepBackupParseError extends Error {\n readonly cause?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = \"KeepBackupParseError\";\n if (options?.cause !== undefined) this.cause = options.cause;\n }\n}\n\nexport class KeepBackupImportError extends Error {\n readonly mode: \"replace\" | \"merge\";\n readonly imported: number;\n readonly failed: number;\n readonly cause?: unknown;\n\n constructor(\n message: string,\n options: {\n mode: \"replace\" | \"merge\";\n imported: number;\n failed: number;\n cause?: unknown;\n },\n ) {\n super(message);\n this.name = \"KeepBackupImportError\";\n this.mode = options.mode;\n this.imported = options.imported;\n this.failed = options.failed;\n if (options.cause !== undefined) this.cause = options.cause;\n }\n}\n\n/** Serialize all adapter data into a versioned JSON backup. */\nexport async function exportItems<TMeta>(adapter: StorageAdapter<TMeta>): Promise<string> {\n const backup: KeepBackup<TMeta> = {\n format: KEEP_BACKUP_FORMAT,\n version: KEEP_BACKUP_VERSION,\n exportedAt: Date.now(),\n items: await adapter.getAll(),\n };\n return JSON.stringify(backup, null, 2);\n}\n\n/** Validate and restore a backup, either replacing or merging existing data. */\nexport async function importItems<TMeta>(\n adapter: StorageAdapter<TMeta>,\n data: string | KeepBackup<TMeta>,\n options: ImportItemsOptions<TMeta> = {},\n): Promise<ImportItemsResult<TMeta>> {\n const backup = parseBackup<TMeta>(data);\n const mode = options.mode ?? \"merge\";\n const validItems: KeepItem<TMeta>[] = [];\n let failed = 0;\n for (const item of backup.items) {\n if (!options.schema) {\n validItems.push(item);\n continue;\n }\n try {\n validItems.push(await validateKeepItem(item, options.schema));\n } catch (cause) {\n options.onInvalidItem?.(cause, item);\n if ((options.invalidItemPolicy ?? \"error\") === \"drop\") {\n failed += 1;\n continue;\n }\n throw cause;\n }\n }\n let items: KeepItem<TMeta>[];\n\n if (mode === \"merge\") {\n try {\n items = await mergeKeepItems(validItems, adapter);\n } catch (cause) {\n throw new KeepBackupImportError(\"KeepKit could not merge the backup.\", {\n mode,\n imported: 0,\n failed: validItems.length + failed,\n cause,\n });\n }\n } else {\n let imported = 0;\n try {\n await adapter.clear();\n for (const item of validItems) {\n await adapter.set(item);\n imported += 1;\n }\n items = await adapter.getAll();\n } catch (cause) {\n throw new KeepBackupImportError(\"KeepKit could not replace the stored items.\", {\n mode,\n imported,\n failed: validItems.length + failed - imported,\n cause,\n });\n }\n }\n\n return { mode, imported: validItems.length, failed, total: items.length, items };\n}\n\nfunction parseBackup<TMeta>(data: string | KeepBackup<TMeta>): KeepBackup<TMeta> {\n let value: unknown = data;\n if (typeof data === \"string\") {\n try {\n value = JSON.parse(data);\n } catch (cause) {\n throw new KeepBackupParseError(\"KeepKit backup is not valid JSON.\", { cause });\n }\n }\n\n if (!isRecord(value)) throw new KeepBackupParseError(\"KeepKit backup must be an object.\");\n if (value.format !== KEEP_BACKUP_FORMAT || value.version !== KEEP_BACKUP_VERSION) {\n throw new KeepBackupParseError(\"KeepKit backup format or version is unsupported.\");\n }\n if (typeof value.exportedAt !== \"number\" || !Number.isFinite(value.exportedAt)) {\n throw new KeepBackupParseError(\"KeepKit backup has an invalid export timestamp.\");\n }\n if (!Array.isArray(value.items) || !value.items.every(isKeepItem)) {\n throw new KeepBackupParseError(\"KeepKit backup contains invalid items.\");\n }\n return value as KeepBackup<TMeta>;\n}\n\nfunction isKeepItem(value: unknown): value is KeepItem {\n if (!isRecord(value)) return false;\n return (\n typeof value.id === \"string\" &&\n typeof value.savedAt === \"number\" &&\n Number.isFinite(value.savedAt) &&\n typeof value.updatedAt === \"number\" &&\n Number.isFinite(value.updatedAt) &&\n \"meta\" in value &&\n (value.targetType === undefined || typeof value.targetType === \"string\") &&\n (value.note === undefined || typeof value.note === \"string\") &&\n (value.schemaVersion === undefined ||\n (typeof value.schemaVersion === \"number\" && Number.isFinite(value.schemaVersion))) &&\n (value.revision === undefined || typeof value.revision === \"string\") &&\n (value.metaUpdatedAt === undefined ||\n (typeof value.metaUpdatedAt === \"number\" && Number.isFinite(value.metaUpdatedAt))) &&\n (value.tags === undefined || (Array.isArray(value.tags) && value.tags.every((tag) => typeof tag === \"string\")))\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n","import type { KeepPlugin, KeepPluginContext } from \"./types\";\n\nexport type KeepInvalidationPluginOptions<TMeta = Record<string, unknown>> = {\n /** Query keys to invalidate after a successful local KeepKit mutation. */\n queryKeys: readonly unknown[] | ((context: KeepPluginContext<TMeta>) => readonly (readonly unknown[])[]);\n /** Connect this callback to queryClient.invalidateQueries or SWR mutate. */\n invalidate: (queryKey: readonly unknown[], context: KeepPluginContext<TMeta>) => void | Promise<void>;\n name?: string;\n};\n\n/** Framework-neutral bridge for TanStack Query, SWR, and similar caches. */\nexport function createKeepInvalidationPlugin<TMeta = Record<string, unknown>>(\n options: KeepInvalidationPluginOptions<TMeta>,\n): KeepPlugin<TMeta> {\n return {\n name: options.name ?? \"keepkit-cache-invalidation\",\n after: async (context) => {\n const keys = typeof options.queryKeys === \"function\" ? options.queryKeys(context) : [options.queryKeys];\n await Promise.all(keys.map((queryKey) => options.invalidate(queryKey, context)));\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/integrations.ts","../src/presets.ts","../src/url.ts"],"sourcesContent":["import type { KeepPlugin, KeepPluginContext } from \"./types\";\n\nexport type KeepInvalidationPluginOptions<TMeta = Record<string, unknown>> = {\n /** Query keys to invalidate after a successful local KeepKit mutation. */\n queryKeys: readonly unknown[] | ((context: KeepPluginContext<TMeta>) => readonly (readonly unknown[])[]);\n /** Connect this callback to queryClient.invalidateQueries or SWR mutate. */\n invalidate: (queryKey: readonly unknown[], context: KeepPluginContext<TMeta>) => void | Promise<void>;\n name?: string;\n};\n\n/** Framework-neutral bridge for TanStack Query, SWR, and similar caches. */\nexport function createKeepInvalidationPlugin<TMeta = Record<string, unknown>>(\n options: KeepInvalidationPluginOptions<TMeta>,\n): KeepPlugin<TMeta> {\n return {\n name: options.name ?? \"keepkit-cache-invalidation\",\n after: async (context) => {\n const keys = typeof options.queryKeys === \"function\" ? options.queryKeys(context) : [options.queryKeys];\n await Promise.all(keys.map((queryKey) => options.invalidate(queryKey, context)));\n },\n };\n}\n","import { exportItems } from \"./backup\";\nimport { createScopedStorageAdapter, type KeepScope } from \"./scope\";\nimport { createBrowserStorageAdapter } from \"./storage/index\";\nimport { SyncStorageAdapter } from \"./storage/sync\";\nimport type { RemoteSyncDriver, StorageAdapter } from \"./types\";\n\nexport type KeepKitPresetMode = \"local\" | \"sync\" | \"backup\";\n\nexport type KeepKitPresetOptions<TMeta = Record<string, unknown>> = {\n mode?: KeepKitPresetMode;\n key?: string;\n scope?: KeepScope;\n remote?: RemoteSyncDriver<TMeta>;\n storage?: StorageAdapter<TMeta>;\n};\n\nexport type KeepKitSetup<TMeta = Record<string, unknown>> = {\n mode: KeepKitPresetMode;\n scope?: KeepScope;\n storage: StorageAdapter<TMeta>;\n exportBackup: () => Promise<string>;\n};\n\n/**\n * Build the recommended local/sync/backup wiring without imposing an auth or\n * API client. Pass the current user and tenant scope whenever the account changes.\n */\nexport function createKeepKitPreset<TMeta = Record<string, unknown>>(\n options: KeepKitPresetOptions<TMeta> = {},\n): KeepKitSetup<TMeta> {\n const mode = options.mode ?? \"local\";\n const local = options.storage\n ? options.scope\n ? createScopedStorageAdapter(options.storage, options.scope)\n : options.storage\n : createBrowserStorageAdapter<TMeta>({ key: options.key, scope: options.scope });\n if (mode === \"sync\" && !options.remote) {\n throw new Error('createKeepKitPreset({ mode: \"sync\" }) requires a remote driver.');\n }\n let storage: StorageAdapter<TMeta> = local;\n if (mode === \"sync\") {\n const remote = options.remote;\n if (!remote) throw new Error('createKeepKitPreset({ mode: \"sync\" }) requires a remote driver.');\n storage = new SyncStorageAdapter<TMeta>({\n local,\n remote,\n userId: options.scope?.userId,\n tenantId: options.scope?.tenantId,\n });\n }\n return {\n mode,\n scope: options.scope,\n storage,\n exportBackup: () => exportItems(storage),\n };\n}\n\nexport const createKeepKitSetup = createKeepKitPreset;\n","import type { KeepListQuery } from \"./query\";\n\nexport type KeepUrlParamNames = {\n search: string;\n tags: string;\n sort: string;\n page: string;\n};\n\nexport type KeepUrlSyncOptions = {\n /** Parameters are intentionally short so shared collection URLs stay readable. */\n params?: Partial<KeepUrlParamNames>;\n /** Push is the default so browser back/forward restores collection states. */\n history?: \"replace\" | \"push\";\n /** URL to read/write. Defaults to the current browser URL. */\n url?: string;\n};\n\nexport const DEFAULT_KEEP_URL_PARAMS: KeepUrlParamNames = {\n search: \"q\",\n tags: \"tag\",\n sort: \"sort\",\n page: \"page\",\n};\n\nexport type KeepUrlState = Pick<KeepListQuery, \"search\" | \"tags\" | \"sort\" | \"pagination\">;\n\n/** Convert a list query to stable URLSearchParams without serializing functions or unsupported filters. */\nexport function encodeKeepListQuery<TMeta = Record<string, unknown>>(\n query: KeepListQuery<TMeta> = {},\n options: Pick<KeepUrlSyncOptions, \"params\"> = {},\n): URLSearchParams {\n const params = { ...DEFAULT_KEEP_URL_PARAMS, ...options.params };\n const result = new URLSearchParams();\n const search = query.search?.query?.trim();\n if (search) result.set(params.search, search);\n for (const tag of query.tags ?? []) {\n const normalized = tag.trim();\n if (normalized) result.append(params.tags, normalized);\n }\n if (query.sort?.by) result.set(params.sort, `${query.sort.by}:${query.sort.direction ?? \"desc\"}`);\n const page = query.pagination?.page;\n if (page !== undefined && Number.isFinite(page) && page > 1) result.set(params.page, String(Math.floor(page)));\n return result;\n}\n\n/** Parse a URL into the query fields supported by KeepCollection. Invalid values are ignored. */\nexport function decodeKeepListQuery(\n input: string | URL | URLSearchParams,\n options: Pick<KeepUrlSyncOptions, \"params\"> = {},\n): KeepUrlState {\n const params = { ...DEFAULT_KEEP_URL_PARAMS, ...options.params };\n const searchParams = input instanceof URLSearchParams ? input : new URL(input, \"http://keepkit.invalid\").searchParams;\n const search = searchParams.get(params.search)?.trim();\n const tags = [\n ...new Set(\n searchParams\n .getAll(params.tags)\n .map((tag) => tag.trim())\n .filter(Boolean),\n ),\n ];\n const sortValue = searchParams.get(params.sort)?.split(\":\");\n const sort: KeepListQuery[\"sort\"] =\n sortValue?.[0] === \"savedAt\" || sortValue?.[0] === \"updatedAt\"\n ? {\n by: sortValue[0],\n direction: sortValue[1] === \"asc\" ? (\"asc\" as const) : (\"desc\" as const),\n }\n : undefined;\n const rawPage = Number(searchParams.get(params.page));\n const page = Number.isInteger(rawPage) && rawPage > 0 ? rawPage : undefined;\n return {\n ...(search ? { search: { query: search } } : {}),\n ...(tags.length > 0 ? { tags } : {}),\n ...(sort ? { sort } : {}),\n ...(page ? { pagination: { page } } : {}),\n };\n}\n\nexport function serializeKeepListQuery<TMeta = Record<string, unknown>>(\n query: KeepListQuery<TMeta> = {},\n options: Pick<KeepUrlSyncOptions, \"params\"> = {},\n): string {\n const value = encodeKeepListQuery(query, options).toString();\n return value ? `?${value}` : \"\";\n}\n\nexport function mergeKeepListQueryFromUrl<TMeta = Record<string, unknown>>(\n query: KeepListQuery<TMeta>,\n input: string | URL | URLSearchParams,\n options: Pick<KeepUrlSyncOptions, \"params\"> = {},\n): KeepListQuery<TMeta> {\n const decoded = decodeKeepListQuery(input, options);\n return {\n ...query,\n ...decoded,\n search: decoded.search ?? query.search,\n tags: decoded.tags ?? query.tags,\n sort: decoded.sort ?? query.sort,\n pagination: decoded.pagination ? { ...query.pagination, ...decoded.pagination } : query.pagination,\n };\n}\n"],"mappings":"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","names":[]}
package/dist/react.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ButtonHTMLAttributes, MouseEvent, HTMLAttributes, ReactElement, ErrorInfo, Component, PropsWithChildren, ComponentType } from 'react';
3
- import { K as KeepItemMetadataRefresher, d as KeepItemRevalidator, R as RevalidateKeepItemsOptions, c as KeepItemRevalidationSummary, f as KeepListQuery, g as KeepStore, h as KeepStoreActions } from './store-CqNh-2DS.js';
4
- export { b as KeepItemRevalidationResult, e as KeepItemStatus, k as isKeepItemMetadataStale } from './store-CqNh-2DS.js';
5
- import { m as KeepItemInput, a as KeepItem, l as KeepEventHandlers, S as StorageAdapter, d as KeepPlugin, K as KeepSchema, b as KeepInvalidItemPolicy, f as KeepChangeContext, t as KeepSyncState } from './types-BCziYE6E.js';
3
+ import { g as KeepItemMetadataRefresher, l as KeepItemRevalidator, R as RevalidateKeepItemsOptions, k as KeepItemRevalidationSummary, m as KeepListQuery, h as KeepItemResolver, c as KeepAutoRevalidationOptions, I as ImportItemsOptions, a as ImportItemsResult, n as KeepStore, o as KeepStoreActions } from './url-BYFEfulm.js';
4
+ export { j as KeepItemRevalidationResult, q as KeepUrlParamNames, r as KeepUrlState, s as KeepUrlSyncOptions, y as isKeepItemMetadataStale } from './url-BYFEfulm.js';
5
+ import { p as KeepItemInput, a as KeepItem, n as KeepEventHandlers, b as StorageAdapter, f as KeepPlugin, K as KeepSchema, o as KeepInvalidItemPolicy, h as KeepChangeContext, x as KeepSyncState, z as KeepUndoState } from './types-D-xRiz1Y.js';
6
+ export { q as KeepItemStatus } from './types-D-xRiz1Y.js';
7
+ export { K as KeepScope, S as ScopedStorageAdapter } from './scope-CtLSlZhq.js';
6
8
 
7
9
  type UseKeepItemResult<TMeta = Record<string, unknown>> = {
8
10
  item: KeepItem<TMeta> | undefined;
@@ -12,6 +14,8 @@ type UseKeepItemResult<TMeta = Record<string, unknown>> = {
12
14
  error: unknown | null;
13
15
  save: () => Promise<void>;
14
16
  remove: () => Promise<void>;
17
+ removeWithUndo: () => Promise<void>;
18
+ undo: () => Promise<void>;
15
19
  toggle: () => Promise<void>;
16
20
  updateNote: (note?: string) => Promise<void>;
17
21
  updateTags: (tags?: string[]) => Promise<void>;
@@ -35,12 +39,14 @@ type UseKeepListResult<TMeta = Record<string, unknown>> = {
35
39
  error: unknown | null;
36
40
  remove: (id: string) => Promise<void>;
37
41
  removeBatch: (ids: string[]) => Promise<void>;
42
+ removeWithUndo: (id: string) => Promise<void>;
43
+ removeBatchWithUndo: (ids: string[]) => Promise<void>;
38
44
  updateTagsBatch: (ids: string[], tags?: string[]) => Promise<void>;
39
45
  addTagsBatch: (ids: string[], tags: string[]) => Promise<void>;
40
46
  removeTagsBatch: (ids: string[], tags: string[]) => Promise<void>;
41
47
  clear: () => Promise<void>;
42
48
  refresh: () => Promise<void>;
43
- revalidate: (revalidator: KeepItemRevalidator<TMeta>, options?: RevalidateKeepItemsOptions) => Promise<KeepItemRevalidationSummary<TMeta>>;
49
+ revalidate: (revalidator: KeepItemRevalidator<TMeta>, options?: RevalidateKeepItemsOptions<TMeta>) => Promise<KeepItemRevalidationSummary<TMeta>>;
44
50
  };
45
51
  declare function useKeepList<TMeta = Record<string, unknown>>(query?: KeepListQuery<TMeta>): UseKeepListResult<TMeta>;
46
52
 
@@ -121,6 +127,7 @@ type KeepContextValue<TMeta = Record<string, unknown>> = {
121
127
  error: unknown | null;
122
128
  lastChange?: KeepChangeContext<TMeta>;
123
129
  syncState: KeepSyncState;
130
+ undo: KeepUndoState;
124
131
  saveItem: (item: KeepItem<TMeta>) => Promise<void>;
125
132
  updateNote: (id: string, note?: string) => Promise<void>;
126
133
  updateTags: (id: string, tags?: string[]) => Promise<void>;
@@ -129,11 +136,16 @@ type KeepContextValue<TMeta = Record<string, unknown>> = {
129
136
  removeTagsBatch: (ids: string[], tags: string[]) => Promise<void>;
130
137
  removeItem: (id: string) => Promise<void>;
131
138
  removeItems: (ids: string[]) => Promise<void>;
139
+ removeItemWithUndo: (id: string) => Promise<void>;
140
+ removeItemsWithUndo: (ids: string[]) => Promise<void>;
141
+ undoLastRemoval: () => Promise<void>;
132
142
  clear: () => Promise<void>;
133
143
  refresh: () => Promise<void>;
134
144
  flushSync: () => Promise<void>;
135
145
  refreshItemMetadata: (id: string, refresh: KeepItemMetadataRefresher<TMeta>) => Promise<void>;
136
- revalidateItems: (revalidator: KeepItemRevalidator<TMeta>, options?: RevalidateKeepItemsOptions) => Promise<KeepItemRevalidationSummary<TMeta>>;
146
+ revalidateItems: (revalidator?: KeepItemRevalidator<TMeta>, options?: RevalidateKeepItemsOptions<TMeta>) => Promise<KeepItemRevalidationSummary<TMeta>>;
147
+ exportBackup: () => Promise<string>;
148
+ importBackup: (data: string, options?: Pick<ImportItemsOptions<TMeta>, "mode" | "invalidItemPolicy" | "onInvalidItem">) => Promise<ImportItemsResult<TMeta>>;
137
149
  };
138
150
  type KeepProviderProps<TMeta = Record<string, unknown>> = PropsWithChildren<KeepEventHandlers<TMeta> & {
139
151
  storage?: StorageAdapter<TMeta>;
@@ -153,12 +165,16 @@ type KeepProviderProps<TMeta = Record<string, unknown>> = PropsWithChildren<Keep
153
165
  fallback?: KeepErrorBoundaryProps["fallback"];
154
166
  onBoundaryError?: KeepErrorBoundaryProps["onError"];
155
167
  boundaryResetKey?: unknown;
168
+ validateItem?: KeepItemRevalidator<TMeta>;
169
+ resolveItem?: KeepItemResolver<TMeta>;
170
+ autoRevalidation?: KeepAutoRevalidationOptions<TMeta>;
171
+ undoTimeoutMs?: number;
156
172
  }>;
157
173
  type KeepStoreAccess<TMeta> = {
158
174
  store: KeepStore<TMeta>;
159
175
  actions: KeepStoreActions<TMeta>;
160
176
  };
161
- declare function KeepProvider<TMeta = Record<string, unknown>>({ storage, initialItems, onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onError, plugins, schemaVersion, schema, invalidItemPolicy, onInvalidItem, migrateMeta, fallback, onBoundaryError, boundaryResetKey, children, }: KeepProviderProps<TMeta>): react.JSX.Element;
177
+ declare function KeepProvider<TMeta = Record<string, unknown>>({ storage, initialItems, onSave, onRemove, onNoteUpdate, onTagsUpdate, onChange, onUndo, onError, plugins, schemaVersion, schema, invalidItemPolicy, onInvalidItem, migrateMeta, fallback, onBoundaryError, boundaryResetKey, validateItem, resolveItem, autoRevalidation, undoTimeoutMs, children, }: KeepProviderProps<TMeta>): react.JSX.Element;
162
178
  declare function useKeepContext<TMeta = Record<string, unknown>>(): KeepContextValue<TMeta>;
163
179
  declare function useKeepStore<TMeta = Record<string, unknown>>(): KeepStoreAccess<TMeta>;
164
180
 
@@ -174,4 +190,4 @@ type KeepKit<TMeta> = {
174
190
  /** Create an app-specific, fully typed set of KeepKit components and hooks. */
175
191
  declare function createKeepKit<TMeta = Record<string, unknown>>(options?: CreateKeepKitOptions<TMeta>): KeepKit<TMeta>;
176
192
 
177
- export { type CreateKeepKitOptions, KeepButton, type KeepButtonItem, type KeepButtonProps, type KeepButtonState, type KeepContextValue, KeepErrorBoundary, type KeepErrorBoundaryProps, KeepItemMetadataRefresher, KeepItemRevalidationSummary, KeepItemRevalidator, type KeepKit, KeepListQuery, KeepProvider, type KeepProviderProps, type KeepShortcutModifier, type KeepShortcutOptions, RevalidateKeepItemsOptions, type UseKeepItemResult, type UseKeepListResult, createKeepKit, useKeepContext, useKeepItem, useKeepList, useKeepShortcut, useKeepStore };
193
+ export { type CreateKeepKitOptions, KeepAutoRevalidationOptions, KeepButton, type KeepButtonItem, type KeepButtonProps, type KeepButtonState, type KeepContextValue, KeepErrorBoundary, type KeepErrorBoundaryProps, KeepItemMetadataRefresher, KeepItemResolver, KeepItemRevalidationSummary, KeepItemRevalidator, type KeepKit, KeepListQuery, KeepProvider, type KeepProviderProps, type KeepShortcutModifier, type KeepShortcutOptions, KeepUndoState, RevalidateKeepItemsOptions, type UseKeepItemResult, type UseKeepListResult, createKeepKit, useKeepContext, useKeepItem, useKeepList, useKeepShortcut, useKeepStore };