Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow passing transaction options to drivers #168

Merged
merged 6 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions docs/content/2.usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,23 @@ await storage.getItem("foo:bar"); // or storage.getItem('/foo/bar')

## Interface

### `hasItem(key)`
### `hasItem(key, opts?)`

Checks if storage contains a key. Resolves to either `true` or `false`.

```js
await storage.hasItem("foo:bar");
```

### `getItem(key)`
### `getItem(key, opts?)`

Gets the value of a key in storage. Resolves to either a javascript primitive value or `undefined`.

```js
await storage.getItem("foo:bar");
```

### `getItemRaw(key)`
### `getItemRaw(key, opts?)`

**Note:** This is an experimental feature. Please check [unjs/unstorage#142](https://github.com/unjs/unstorage/issues/142) for more information.

Expand All @@ -61,7 +61,7 @@ Gets the value of a key in storage in raw format.
const value = await storage.getItemRaw("foo:bar.bin");
```

### `setItem(key, value)`
### `setItem(key, value, opts?)`

Add/Update a value to the storage.

Expand All @@ -73,7 +73,7 @@ If value is `undefined`, it is same as calling `removeItem(key)`.
await storage.setItem("foo:bar", "baz");
```

### `setItemRaw(key, value)`
### `setItemRaw(key, value, opts?)`

**Note:** This is an experimental feature. Please check [unjs/unstorage#142](https://github.com/unjs/unstorage/issues/142) for more information.

Expand All @@ -85,15 +85,15 @@ If value is `undefined`, it is same as calling `removeItem(key)`.
await storage.setItemRaw("data/test.bin", new Uint8Array([1, 2, 3]));
```

### `removeItem(key, removeMeta = true)`
### `removeItem(key, opts = { removeMeta = true })`

Remove a value (and it's meta) from storage.

```js
await storage.removeItem("foo:bar");
```

### `getMeta(key, nativeOnly?)`
### `getMeta(key, opts = { nativeOnly? })`

Get metadata object for a specific key.

Expand All @@ -106,7 +106,7 @@ This data is fetched from two sources:
await storage.getMeta("foo:bar"); // For fs driver returns an object like { mtime, atime, size }
```

### `setMeta(key)`
### `setMeta(key, opts?)`

Set custom meta for a specific key by adding a `$` suffix.

Expand All @@ -115,7 +115,7 @@ await storage.setMeta("foo:bar", { flag: 1 });
// Same as storage.setItem('foo:bar$', { flag: 1 })
```

### `removeMeta(key)`
### `removeMeta(key, opts?)`

Remove meta for a specific key by adding a `$` suffix.

Expand All @@ -124,7 +124,7 @@ await storage.removeMeta("foo:bar");
// Same as storage.removeItem('foo:bar$')
```

### `getKeys(base?)`
### `getKeys(base?, opts?)`

Get all keys. Returns an array of strings.

Expand All @@ -136,7 +136,7 @@ If a base is provided, only keys starting with the base will be returned also on
await storage.getKeys();
```

### `clear(base?)`
### `clear(base?, opts?)`

Removes all stored key/values. If a base is provided, only mounts matching base will be cleared.

Expand Down
14 changes: 7 additions & 7 deletions docs/content/5.custom-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import { createStorage, defineDriver } from "unstorage";

const myStorageDriver = defineDriver((_opts) => {
return {
async hasItem(key) {},
async getItem(key) {},
async setItem(key, value) {},
async removeItem(key) {},
async getKeys() {},
async clear() {},
async hasItem(key, opts) {},
async getItem(key, opts) {},
async setItem(key, value, opts) {},
async removeItem(key, opts) {},
async getKeys(base, opts) {},
async clear(base, opts) {},
async dispose() {},
// async watch(callback) {}
async watch(callback) {},
};
});

Expand Down
75 changes: 43 additions & 32 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,29 +103,29 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {

const storage: Storage = {
// Item
hasItem(key) {
hasItem(key, opts = {}) {
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
return asyncCall(driver.hasItem, relativeKey);
return asyncCall(driver.hasItem, relativeKey, opts);
},
getItem(key) {
getItem(key, opts = {}) {
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
return asyncCall(driver.getItem, relativeKey).then((value) =>
return asyncCall(driver.getItem, relativeKey, opts).then((value) =>
destr(value)
);
},
getItemRaw(key) {
getItemRaw(key, opts = {}) {
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
if (driver.getItemRaw) {
return asyncCall(driver.getItemRaw, relativeKey);
return asyncCall(driver.getItemRaw, relativeKey, opts);
}
return asyncCall(driver.getItem, relativeKey).then((value) =>
return asyncCall(driver.getItem, relativeKey, opts).then((value) =>
deserializeRaw(value)
);
},
async setItem(key, value) {
async setItem(key, value, opts = {}) {
if (value === undefined) {
return storage.removeItem(key);
}
Expand All @@ -134,54 +134,64 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {
if (!driver.setItem) {
return; // Readonly
}
await asyncCall(driver.setItem, relativeKey, stringify(value));
await asyncCall(driver.setItem, relativeKey, stringify(value), opts);
if (!driver.watch) {
onChange("update", key);
}
},
async setItemRaw(key, value) {
async setItemRaw(key, value, opts = {}) {
if (value === undefined) {
return storage.removeItem(key);
return storage.removeItem(key, opts);
}
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
if (driver.setItemRaw) {
await asyncCall(driver.setItemRaw, relativeKey, value);
await asyncCall(driver.setItemRaw, relativeKey, value, opts);
} else if (driver.setItem) {
await asyncCall(driver.setItem, relativeKey, serializeRaw(value));
await asyncCall(driver.setItem, relativeKey, serializeRaw(value), opts);
} else {
return; // Readonly
}
if (!driver.watch) {
onChange("update", key);
}
},
async removeItem(key, removeMeta = true) {
async removeItem(key, opts = {}) {
// TODO: Remove in next major version
if (typeof opts === "boolean") {
opts = { removeMata: opts };
}
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
if (!driver.removeItem) {
return; // Readonly
}
await asyncCall(driver.removeItem, relativeKey);
if (removeMeta) {
await asyncCall(driver.removeItem, relativeKey + "$");
await asyncCall(driver.removeItem, relativeKey, opts);
if (opts.removeMata) {
await asyncCall(driver.removeItem, relativeKey + "$", opts);
}
if (!driver.watch) {
onChange("remove", key);
}
},
// Meta
async getMeta(key, nativeMetaOnly) {
async getMeta(key, opts = {}) {
// TODO: Remove in next major version
if (typeof opts === "boolean") {
opts = { nativeOnly: opts };
}
key = normalizeKey(key);
const { relativeKey, driver } = getMount(key);
const meta = Object.create(null);
if (driver.getMeta) {
Object.assign(meta, await asyncCall(driver.getMeta, relativeKey));
Object.assign(meta, await asyncCall(driver.getMeta, relativeKey, opts));
}
if (!nativeMetaOnly) {
const value = await asyncCall(driver.getItem, relativeKey + "$").then(
(value_) => destr(value_)
);
if (!opts.nativeOnly) {
const value = await asyncCall(
driver.getItem,
relativeKey + "$",
opts
).then((value_) => destr(value_));
if (value && typeof value === "object") {
// TODO: Support date by destr?
if (typeof value.atime === "string") {
Expand All @@ -195,22 +205,23 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {
}
return meta;
},
setMeta(key: string, value: any) {
return this.setItem(key + "$", value);
setMeta(key: string, value: any, opts = {}) {
return this.setItem(key + "$", value, opts);
},
removeMeta(key: string) {
return this.removeItem(key + "$");
removeMeta(key: string, opts = {}) {
return this.removeItem(key + "$", opts);
},
// Keys
async getKeys(base) {
async getKeys(base, opts = {}) {
base = normalizeBaseKey(base);
const mounts = getMounts(base, true);
let maskedMounts = [];
const allKeys = [];
for (const mount of mounts) {
const rawKeys = await asyncCall(
mount.driver.getKeys,
mount.relativeBase
mount.relativeBase,
opts
);
const keys = rawKeys
.map((key) => mount.mountpoint + normalizeKey(key))
Expand All @@ -229,16 +240,16 @@ export function createStorage(options: CreateStorageOptions = {}): Storage {
: allKeys.filter((key) => !key.endsWith("$"));
},
// Utils
async clear(base) {
async clear(base, opts = {}) {
base = normalizeBaseKey(base);
await Promise.all(
getMounts(base, false).map(async (m) => {
if (m.driver.clear) {
return asyncCall(m.driver.clear);
return asyncCall(m.driver.clear, m.relativeBase, opts);
}
// Fallback to remove all keys if clear not implemented
if (m.driver.removeItem) {
const keys = await m.driver.getKeys();
const keys = await m.driver.getKeys(m.relativeBase, opts);
return Promise.all(keys.map((key) => m.driver.removeItem!(key)));
}
// Readonly
Expand Down
81 changes: 61 additions & 20 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,81 @@ export interface StorageMeta {
[key: string]: StorageValue | Date | undefined;
}

type TransactionOptions = Record<string, any>;

export interface Driver {
hasItem: (key: string) => MaybePromise<boolean>;
getItem: (key: string) => MaybePromise<StorageValue>;
hasItem: (key: string, opts?: TransactionOptions) => MaybePromise<boolean>;
getItem: (
key: string,
opts?: TransactionOptions
) => MaybePromise<StorageValue>;
/** @experimental */
getItemRaw?: (key: string) => MaybePromise<unknown>;
setItem?: (key: string, value: string) => MaybePromise<void>;
getItemRaw?: (
key: string,
opts?: TransactionOptions
) => MaybePromise<unknown>;
setItem?: (
key: string,
value: string,
opts?: TransactionOptions
) => MaybePromise<void>;
/** @experimental */
setItemRaw?: (key: string, value: any) => MaybePromise<void>;
removeItem?: (key: string) => MaybePromise<void>;
getMeta?: (key: string) => MaybePromise<StorageMeta>;
getKeys: (base?: string) => MaybePromise<string[]>;
clear?: () => MaybePromise<void>;
setItemRaw?: (
key: string,
value: any,
opts?: TransactionOptions
) => MaybePromise<void>;
removeItem?: (key: string, opts?: TransactionOptions) => MaybePromise<void>;
getMeta?: (
key: string,
opts?: TransactionOptions
) => MaybePromise<StorageMeta>;
getKeys: (base?: string, opts?: TransactionOptions) => MaybePromise<string[]>;
clear?: (base?: string, opts?: TransactionOptions) => MaybePromise<void>;
dispose?: () => MaybePromise<void>;
watch?: (callback: WatchCallback) => MaybePromise<Unwatch>;
}

export interface Storage {
// Item
hasItem: (key: string) => Promise<boolean>;
getItem: (key: string) => Promise<StorageValue>;
hasItem: (key: string, opts?: TransactionOptions) => Promise<boolean>;
getItem: (key: string, opts?: TransactionOptions) => Promise<StorageValue>;
/** @experimental See https://github.com/unjs/unstorage/issues/142 */
getItemRaw: (key: string) => Promise<any>;
setItem: (key: string, value: StorageValue) => Promise<void>;
getItemRaw: (key: string, opts?: TransactionOptions) => Promise<any>;
setItem: (
key: string,
value: StorageValue,
opts?: TransactionOptions
) => Promise<void>;
/** @experimental See https://github.com/unjs/unstorage/issues/142 */
setItemRaw: (key: string, value: any) => Promise<void>;
removeItem: (key: string, removeMeta?: boolean) => Promise<void>;
setItemRaw: (
key: string,
value: any,
opts?: TransactionOptions
) => Promise<void>;
removeItem: (
key: string,
opts?:
| (TransactionOptions & { removeMata?: boolean })
| boolean /* legacy: removeMata */
) => Promise<void>;
// Meta
getMeta: (key: string, nativeMetaOnly?: true) => MaybePromise<StorageMeta>;
setMeta: (key: string, value: StorageMeta) => Promise<void>;
removeMeta: (key: string) => Promise<void>;
getMeta: (
key: string,
opts?:
| (TransactionOptions & { nativeOnly?: boolean })
| boolean /* legacy: nativeOnly */
) => MaybePromise<StorageMeta>;
setMeta: (
key: string,
value: StorageMeta,
opts?: TransactionOptions
) => Promise<void>;
removeMeta: (key: string, opts?: TransactionOptions) => Promise<void>;
// Keys
getKeys: (base?: string) => Promise<string[]>;
getKeys: (base?: string, opts?: TransactionOptions) => Promise<string[]>;
// Utils
clear: (base?: string) => Promise<void>;
clear: (base?: string, opts?: TransactionOptions) => Promise<void>;
dispose: () => Promise<void>;
watch: (callback: WatchCallback) => Promise<Unwatch>;
unwatch: () => Promise<void>;
Expand Down