Skip to content

Commit

Permalink
feat: mount improvements and unmount
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Mar 11, 2021
1 parent 7052380 commit 7dd731b
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 42 deletions.
40 changes: 21 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@

WIP:

- Unmount
- State watching
- Key expiration
- State compression
- Watcher
- Storage server

## Drivers

Expand Down Expand Up @@ -113,6 +112,23 @@ If a base is provided, only keys starting with base will be returned also only m
await storage.getKeys()
```

### `storage.clear(base?)`

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

```js
await storage.clear()
```

### `storage.dispose()`

Disposes all mounted storages to ensure there are no open-handles left. Call it before exiting process.

**Note:** Dispose also clears in-memory data.

```js
await storage.dispose()
```

### `storage.mount(mountpoint, driver, items?)`

Expand Down Expand Up @@ -140,13 +156,9 @@ await storage.setItem('/output/test', 'works')
await storage.setItem('/foo', 'bar')
```

### `storage.clear(base?)`

Removes all stored key/values. If a base is provided, only mounts matching base will be cleared.
### `storage.unmount(mountpoint, dispose = true)`

```js
await storage.clear()
```
Unregisters a mountpoint. Has no effect if mountpoint is not found or is root.

### `snapshot(storage, base?)`

Expand All @@ -157,16 +169,6 @@ import { snapshot } from 'unstorage'
const data = await snapshot(storage, '/etc')
```

### `storage.dispose()`

Disposes all mounted storages to ensure there are no open-handles left. Call it before exiting process.

**Note:** Dispose also clears in-memory data.

```js
await storage.dispose()
```

## Contribution

- Clone repository
Expand Down
8 changes: 4 additions & 4 deletions src/drivers/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ export default <DriverFactory> function () {
getItem (key) {
return data.get(key) || null
},
setItem (key, value) {
setItem(key, value) {
data.set(key, value)
},
removeItem (key) {
data.delete(key)
},
getKeys () {
getKeys() {
return Array.from(data.keys())
},
clear () {
clear() {
data.clear()
},
dispose () {
dispose() {
data.clear()
}
}
Expand Down
43 changes: 30 additions & 13 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import { normalizeKey, normalizeBase, asyncCall, stringify } from './utils'

interface StorageCTX {
mounts: Record<string, Driver>
mountKeys: string[]
mountpoints: string[]
}

export function createStorage (): Storage {
const ctx: StorageCTX = {
mounts: { '': memory() },
mountKeys: ['']
mountpoints: ['']
}

const getMount = (key?: string) => {
key = normalizeKey(key)
for (const base of ctx.mountKeys) {
for (const base of ctx.mountpoints) {
if (key.startsWith(base)) {
return {
relativeKey: key.substr(base.length),
Expand All @@ -32,9 +32,12 @@ export function createStorage (): Storage {

const getMounts = (base?: string) => {
base = normalizeBase(base)
return ctx.mountKeys
.filter(key => base!.startsWith(key))
.map(key => getMount(key))
return ctx.mountpoints
.filter(mountpoint => base!.startsWith(mountpoint))
.map(mountpoint => ({
mountpoint,
driver: ctx.mounts[mountpoint]
}))
}

const storage: Storage = {
Expand Down Expand Up @@ -63,8 +66,11 @@ export function createStorage (): Storage {
},
async getKeys (base) {
base = normalizeBase(base)
const rawKeys = await Promise.all(getMounts(base).map(m => asyncCall(m.driver.getKeys)))
const keys = rawKeys.flat().map(key => normalizeKey(key))
const keyGroups = await Promise.all(getMounts(base).map(async (mount) => {
const rawKeys = await asyncCall(mount.driver.getKeys)
return rawKeys.map(key => mount.mountpoint + normalizeKey(key))
}))
const keys = keyGroups.flat()
return base ? keys.filter(key => key.startsWith(base!)) : keys
},
async clear (base) {
Expand All @@ -74,22 +80,33 @@ export function createStorage (): Storage {
await Promise.all(Object.values(ctx.mounts).map(driver => dispose(driver)))
},
async mount (base, driver, initialState) {
base = normalizeKey(base)
if (!ctx.mountKeys.includes(base)) {
ctx.mountKeys.push(base)
ctx.mountKeys.sort((a, b) => b.length - a.length)
base = normalizeBase(base)
if (!ctx.mountpoints.includes(base)) {
ctx.mountpoints.push(base)
ctx.mountpoints.sort((a, b) => b.length - a.length)
}
if (ctx.mounts[base]) {
if (ctx.mounts[base].dispose) {
// eslint-disable-next-line no-console
dispose(ctx.mounts[base]!).catch(console.error)
await dispose(ctx.mounts[base]!)
}
delete ctx.mounts[base]
}
ctx.mounts[base] = driver
if (initialState) {
await storage.setItems(base, initialState)
}
},
async unmount (base: string, _dispose = true) {
base = normalizeBase(base)
if (!base /* root */ || !ctx.mounts[base]) {
return
}
if (_dispose) {
await dispose(ctx.mounts[base])
}
ctx.mountpoints = ctx.mountpoints.filter(key => key !== base)
delete ctx.mounts[base]
}
}

Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ export interface Storage {
getKeys: (base?: string) => Promise<string[]>
clear: (base?: string) => Promise<void>
mount: (base: string, driver: Driver, initialState?: Record<string, string>) => Promise<void>
unmount: (base: string, dispose?: boolean) => Promise<void>
dispose: () => Promise<void>
}
File renamed without changes.
13 changes: 7 additions & 6 deletions test/snapshot.test.ts → test/storage.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { createStorage, snapshot } from '../src'
import memory from '../src/drivers/memory'

const data = {
'etc:conf': 'test',
'data:foo': 'bar'
}

describe('snapshot', () => {
it('snapshot', async () => {
describe('storage', () => {
it('mount/unmount', async () => {
const storage = createStorage()
await storage.setItems('', data)
expect(await snapshot(storage, '')).toMatchObject(data)
await storage.mount('/mnt', memory(), data)
expect(await snapshot(storage, '/mnt')).toMatchObject(data)
})

it('snapshot (subpath)', async () => {
it('snapshot', async () => {
const storage = createStorage()
await storage.setItems('', data)
expect(await snapshot(storage, 'etc')).toMatchObject({ conf: 'test' })
expect(await snapshot(storage, '')).toMatchObject(data)
})
})

0 comments on commit 7dd731b

Please sign in to comment.