@kvs/storage
Version:
Storage(localStorage, sessionStorage) helper for KVS.
173 lines • 5.99 kB
JavaScript
function invariant(condition, message) {
if (condition) {
return;
}
throw new Error(message);
}
const TABLE_KEY_MARKER = ".__.";
export const getItem = (storage, tableName, key) => {
const storageKey = `${tableName}${TABLE_KEY_MARKER}${String(key)}`;
const item = storage.getItem(storageKey);
return item !== null ? JSON.parse(item) : undefined;
};
export const hasItem = (storage, tableName, key) => {
const storageKey = `${tableName}${TABLE_KEY_MARKER}${String(key)}`;
return storage.getItem(storageKey) !== null;
};
export const setItem = (storage, tableName, key, value) => {
// It is difference with IndexedDB implementation.
// This behavior compatible with localStorage.
if (value === undefined) {
return deleteItem(storage, tableName, key);
}
const storageKey = `${tableName}${TABLE_KEY_MARKER}${String(key)}`;
return storage.setItem(storageKey, JSON.stringify(value));
};
export const clearItem = (storage, tableName, kvsVersionKey, options) => {
// TODO: kvsVersionKey is special type
const currentVersion = getItem(storage, tableName, kvsVersionKey);
// clear all
storage.clear();
// if option.force is true, does not restore metadata.
if (options.force) {
return;
}
// set kvs version again
if (currentVersion !== undefined) {
setItem(storage, tableName, kvsVersionKey, currentVersion);
}
};
export const deleteItem = (storage, tableName, key) => {
const storageKey = `${tableName}${TABLE_KEY_MARKER}${String(key)}`;
try {
storage.removeItem(storageKey);
return true;
}
catch (_a) {
return false;
}
};
export function* createIterator(storage, tableName, kvsVersionKey) {
const tableKeyPrefix = `${tableName}${TABLE_KEY_MARKER}`;
for (let i = 0; i < storage.length; i++) {
const key = storage.key(i);
if (!key) {
continue;
}
// skip another storage
if (!key.startsWith(tableKeyPrefix)) {
continue;
}
// skip meta key
const keyWithoutPrefix = key.replace(tableKeyPrefix, "");
if (keyWithoutPrefix === kvsVersionKey) {
continue;
}
const value = getItem(storage, tableName, keyWithoutPrefix);
yield [keyWithoutPrefix, value];
}
}
const DEFAULT_KVS_VERSION = 1;
const openStorage = async ({ storage, version, tableName, kvsVersionKey, onUpgrade }) => {
// kvsVersionKey is special type
// first `oldVersion` is `0`
let oldVersion = getItem(storage, tableName, kvsVersionKey);
if (oldVersion === undefined) {
setItem(storage, tableName, kvsVersionKey, DEFAULT_KVS_VERSION);
// first `oldVersion` is `0`
// https://github.com/azu/kvs/issues/8
oldVersion = 0;
}
// if user set newVersion, upgrade it
if (oldVersion !== version) {
await onUpgrade({
oldVersion,
newVersion: version,
storage
});
// save current version if upgrade is success
setItem(storage, tableName, kvsVersionKey, version);
return storage;
}
return storage;
};
const createStore = ({ tableName, storage, kvsVersionKey }) => {
const store = {
get(key) {
return Promise.resolve().then(() => {
return getItem(storage, tableName, key);
});
},
has(key) {
return Promise.resolve().then(() => {
return hasItem(storage, tableName, key);
});
},
set(key, value) {
return Promise.resolve()
.then(() => {
return setItem(storage, tableName, key, value);
})
.then(() => {
return store;
});
},
clear() {
return Promise.resolve().then(() => {
return clearItem(storage, tableName, kvsVersionKey, { force: false });
});
},
delete(key) {
return Promise.resolve().then(() => {
return deleteItem(storage, tableName, key);
});
},
dropInstance() {
return Promise.resolve().then(() => {
return clearItem(storage, tableName, kvsVersionKey, { force: true });
});
},
close() {
// Noop function
return Promise.resolve();
},
[Symbol.asyncIterator]() {
const iterator = createIterator(storage, tableName, kvsVersionKey);
return {
next() {
return Promise.resolve().then(() => {
return iterator.next();
});
}
};
}
};
return store;
};
export const kvsStorage = async (options) => {
var _a;
const { name, version, upgrade, ...kvStorageOptions } = options;
invariant(typeof name === "string", "name should be string");
invariant(name.length > 0, "name should not be empty");
invariant(!name.includes(TABLE_KEY_MARKER), `name can not include ${TABLE_KEY_MARKER}. It is reserved in kvs.`);
invariant(typeof version === "number", `version should be number`);
const kvsVersionKey = (_a = kvStorageOptions.kvsVersionKey) !== null && _a !== void 0 ? _a : "__kvs_version__";
const storage = await openStorage({
storage: options.storage,
version: options.version,
tableName: name,
onUpgrade: ({ oldVersion, newVersion, storage }) => {
if (!options.upgrade) {
return;
}
return options.upgrade({
kvs: createStore({ tableName: name, storage, kvsVersionKey }),
oldVersion,
newVersion
});
},
kvsVersionKey
});
return createStore({ tableName: name, storage, kvsVersionKey });
};
//# sourceMappingURL=storage.js.map