mirror of
https://github.com/unjs/unstorage.git
synced 2025-12-08 21:26:09 +00:00
refactor: update repository
This commit is contained in:
parent
5ac62d7b45
commit
ae352daa68
@ -1,5 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"@nuxtjs/eslint-config-typescript"
|
||||
]
|
||||
"eslint-config-unjs"
|
||||
],
|
||||
"rules": {
|
||||
"unicorn/no-null": 0,
|
||||
"unicorn/prevent-abbreviations": 0
|
||||
}
|
||||
}
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 - UnJS
|
||||
Copyright (c) Pooya Parsa <pooya@pi0.io>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@ -113,7 +113,7 @@ await storage.hasItem('foo:bar')
|
||||
|
||||
### `storage.getItem(key)`
|
||||
|
||||
Gets the value of a key in storage. Resolves to either `string` or `null`.
|
||||
Gets the value of a key in storage. Resolves to either `string` or `undefined`.
|
||||
|
||||
```js
|
||||
await storage.getItem('foo:bar')
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
import { defineBuildConfig } from "unbuild";
|
||||
|
||||
export default defineBuildConfig({
|
||||
declaration: true,
|
||||
@ -6,9 +6,9 @@ export default defineBuildConfig({
|
||||
emitCJS: true
|
||||
},
|
||||
entries: [
|
||||
'src/index',
|
||||
'src/server',
|
||||
{ input: 'src/drivers/', outDir: 'dist/drivers', format: 'esm' },
|
||||
{ input: 'src/drivers/', outDir: 'dist/drivers', format: 'cjs', ext: 'cjs', declaration: false }
|
||||
"src/index",
|
||||
"src/server",
|
||||
{ input: "src/drivers/", outDir: "dist/drivers", format: "esm" },
|
||||
{ input: "src/drivers/", outDir: "dist/drivers", format: "cjs", ext: "cjs", declaration: false }
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
@ -1,27 +1,28 @@
|
||||
import { resolve } from 'path'
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { createStorage } from '../src'
|
||||
import { createStorageServer } from '../src/server'
|
||||
import fsdriver from '../src/drivers/fs'
|
||||
import { resolve } from "node:path";
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import { createStorage } from "../src";
|
||||
import { createStorageServer } from "../src/server";
|
||||
import fsdriver from "../src/drivers/fs";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'node-fetch': 'node-fetch/browser'
|
||||
"node-fetch": "node-fetch/browser"
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
{
|
||||
name: 'app',
|
||||
name: "app",
|
||||
configureServer (server) {
|
||||
const storage = createStorage()
|
||||
const storageServer = createStorageServer(storage)
|
||||
storage.mount('/src', fsdriver({ base: resolve(__dirname, '..') }))
|
||||
server.middlewares.use('/storage', storageServer.handle)
|
||||
const storage = createStorage();
|
||||
const storageServer = createStorageServer(storage);
|
||||
// eslint-disable-next-line unicorn/prefer-module
|
||||
storage.mount("/src", fsdriver({ base: resolve(__dirname, "..") }));
|
||||
server.middlewares.use("/storage", storageServer.handle);
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
@ -13,7 +13,8 @@
|
||||
},
|
||||
".": {
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
"require": "./dist/index.cjs",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./server": {
|
||||
"import": "./dist/server.mjs",
|
||||
@ -44,7 +45,7 @@
|
||||
"destr": "^1.2.1",
|
||||
"h3": "^0.8.6",
|
||||
"ioredis": "^5.2.4",
|
||||
"listhen": "^0.3.5",
|
||||
"listhen": "^1.0.0",
|
||||
"mkdir": "^0.0.2",
|
||||
"mri": "^1.2.0",
|
||||
"ohmyfetch": "^0.4.21",
|
||||
@ -53,8 +54,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^3.18.0",
|
||||
"@nuxtjs/eslint-config-typescript": "^11.0.0",
|
||||
"@types/ioredis": "^4.28.10",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/mri": "^1.1.1",
|
||||
"@types/node": "^18.11.9",
|
||||
@ -66,6 +65,7 @@
|
||||
"changelogen": "^0.4.0",
|
||||
"doctoc": "^2.2.1",
|
||||
"eslint": "^8.27.0",
|
||||
"eslint-config-unjs": "^0.0.2",
|
||||
"jiti": "^1.16.0",
|
||||
"jsdom": "^20.0.2",
|
||||
"mkdist": "latest",
|
||||
|
||||
158
pnpm-lock.yaml
generated
158
pnpm-lock.yaml
generated
@ -2,8 +2,6 @@ lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@cloudflare/workers-types': ^3.18.0
|
||||
'@nuxtjs/eslint-config-typescript': ^11.0.0
|
||||
'@types/ioredis': ^4.28.10
|
||||
'@types/jsdom': ^20.0.1
|
||||
'@types/mri': ^1.1.1
|
||||
'@types/node': ^18.11.9
|
||||
@ -18,11 +16,12 @@ specifiers:
|
||||
destr: ^1.2.1
|
||||
doctoc: ^2.2.1
|
||||
eslint: ^8.27.0
|
||||
eslint-config-unjs: ^0.0.2
|
||||
h3: ^0.8.6
|
||||
ioredis: ^5.2.4
|
||||
jiti: ^1.16.0
|
||||
jsdom: ^20.0.2
|
||||
listhen: ^0.3.5
|
||||
listhen: ^1.0.0
|
||||
mkdir: ^0.0.2
|
||||
mkdist: latest
|
||||
monaco-editor: ^0.34.1
|
||||
@ -44,7 +43,7 @@ dependencies:
|
||||
destr: 1.2.1
|
||||
h3: 0.8.6
|
||||
ioredis: 5.2.4
|
||||
listhen: 0.3.5
|
||||
listhen: 1.0.0
|
||||
mkdir: 0.0.2
|
||||
mri: 1.2.0
|
||||
ohmyfetch: 0.4.21
|
||||
@ -53,8 +52,6 @@ dependencies:
|
||||
|
||||
devDependencies:
|
||||
'@cloudflare/workers-types': 3.18.0
|
||||
'@nuxtjs/eslint-config-typescript': 11.0.0_rmayb2veg2btbq6mbmnyivgasy
|
||||
'@types/ioredis': 4.28.10
|
||||
'@types/jsdom': 20.0.1
|
||||
'@types/mri': 1.1.1
|
||||
'@types/node': 18.11.9
|
||||
@ -66,6 +63,7 @@ devDependencies:
|
||||
changelogen: 0.4.0
|
||||
doctoc: 2.2.1
|
||||
eslint: 8.27.0
|
||||
eslint-config-unjs: 0.0.2_rmayb2veg2btbq6mbmnyivgasy
|
||||
jiti: 1.16.0
|
||||
jsdom: 20.0.2
|
||||
mkdist: 0.4.0_typescript@4.8.4
|
||||
@ -459,43 +457,6 @@ packages:
|
||||
fastq: 1.13.0
|
||||
dev: true
|
||||
|
||||
/@nuxtjs/eslint-config-typescript/11.0.0_rmayb2veg2btbq6mbmnyivgasy:
|
||||
resolution: {integrity: sha512-hmFjGtXT524ql8eTbK8BaRkamcXB6Z8YOW8nSQhosTP6oBw9WtOFUeWr7holyE278UhOmx+wDFG90BnyM9D+UA==}
|
||||
peerDependencies:
|
||||
eslint: ^8.23.0
|
||||
dependencies:
|
||||
'@nuxtjs/eslint-config': 11.0.0_atoydicabq4zoeebkyg3dz56cy
|
||||
'@typescript-eslint/eslint-plugin': 5.39.0_7f3aqhlvvz4evw3j7e7r2zx4rq
|
||||
'@typescript-eslint/parser': 5.39.0_rmayb2veg2btbq6mbmnyivgasy
|
||||
eslint: 8.27.0
|
||||
eslint-import-resolver-typescript: 3.5.1_dcpv4nbdr5ks2h5677xdltrk6e
|
||||
eslint-plugin-import: 2.26.0_atoydicabq4zoeebkyg3dz56cy
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@nuxtjs/eslint-config/11.0.0_atoydicabq4zoeebkyg3dz56cy:
|
||||
resolution: {integrity: sha512-o4zFOpU8gJgwrC/gLE7c2E0XEjkv2fEixCGG1y+dZYzBPyzTorkQmfxskSF3WRXcZkpkS9uUYlRkeOSdYB7z0w==}
|
||||
peerDependencies:
|
||||
eslint: ^8.23.0
|
||||
dependencies:
|
||||
eslint: 8.27.0
|
||||
eslint-config-standard: 17.0.0_5p2jx74osvs7etmizpb2fndoma
|
||||
eslint-plugin-import: 2.26.0_atoydicabq4zoeebkyg3dz56cy
|
||||
eslint-plugin-n: 15.3.0_eslint@8.27.0
|
||||
eslint-plugin-node: 11.1.0_eslint@8.27.0
|
||||
eslint-plugin-promise: 6.0.1_eslint@8.27.0
|
||||
eslint-plugin-unicorn: 43.0.2_eslint@8.27.0
|
||||
eslint-plugin-vue: 9.6.0_eslint@8.27.0
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/parser'
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@open-draft/until/1.0.3:
|
||||
resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==}
|
||||
dev: true
|
||||
@ -659,12 +620,6 @@ packages:
|
||||
resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
|
||||
dev: true
|
||||
|
||||
/@types/ioredis/4.28.10:
|
||||
resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==}
|
||||
dependencies:
|
||||
'@types/node': 18.11.9
|
||||
dev: true
|
||||
|
||||
/@types/istanbul-lib-coverage/2.0.4:
|
||||
resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
|
||||
dev: true
|
||||
@ -1143,10 +1098,6 @@ packages:
|
||||
readable-stream: 3.6.0
|
||||
dev: true
|
||||
|
||||
/boolbase/1.0.0:
|
||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||
dev: true
|
||||
|
||||
/brace-expansion/1.1.11:
|
||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||
dependencies:
|
||||
@ -1476,12 +1427,6 @@ packages:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
/cssesc/3.0.0:
|
||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/cssom/0.3.8:
|
||||
resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
|
||||
dev: true
|
||||
@ -1584,6 +1529,11 @@ packages:
|
||||
|
||||
/defu/6.1.0:
|
||||
resolution: {integrity: sha512-pOFYRTIhoKujrmbTRhcW5lYQLBXw/dlTwfI8IguF1QCDJOcJzNH1w+YFjxqy6BAuJrClTy6MUE8q+oKJ2FLsIw==}
|
||||
dev: true
|
||||
|
||||
/defu/6.1.1:
|
||||
resolution: {integrity: sha512-aA964RUCsBt0FGoNIlA3uFgo2hO+WWC0fiC6DBps/0SFzkKcYoM/3CzVLIa5xSsrFjdioMdYgAIbwo80qp2MoA==}
|
||||
dev: false
|
||||
|
||||
/delayed-stream/1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
@ -2412,6 +2362,15 @@ packages:
|
||||
source-map: 0.6.1
|
||||
dev: true
|
||||
|
||||
/eslint-config-prettier/8.5.0_eslint@8.27.0:
|
||||
resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
eslint: '>=7.0.0'
|
||||
dependencies:
|
||||
eslint: 8.27.0
|
||||
dev: true
|
||||
|
||||
/eslint-config-standard/17.0.0_5p2jx74osvs7etmizpb2fndoma:
|
||||
resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==}
|
||||
peerDependencies:
|
||||
@ -2426,6 +2385,29 @@ packages:
|
||||
eslint-plugin-promise: 6.0.1_eslint@8.27.0
|
||||
dev: true
|
||||
|
||||
/eslint-config-unjs/0.0.2_rmayb2veg2btbq6mbmnyivgasy:
|
||||
resolution: {integrity: sha512-6zhni5Fdlpu/DJ+hfcbNEqmUixhGujBQXXB7N9SdZcvh135RlFTNoqor7sowP1ttd0lDY4lTewUzUjXSOVB7Ww==}
|
||||
peerDependencies:
|
||||
eslint: '*'
|
||||
typescript: '*'
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.39.0_7f3aqhlvvz4evw3j7e7r2zx4rq
|
||||
'@typescript-eslint/parser': 5.39.0_rmayb2veg2btbq6mbmnyivgasy
|
||||
eslint: 8.27.0
|
||||
eslint-config-prettier: 8.5.0_eslint@8.27.0
|
||||
eslint-config-standard: 17.0.0_5p2jx74osvs7etmizpb2fndoma
|
||||
eslint-import-resolver-typescript: 3.5.1_dcpv4nbdr5ks2h5677xdltrk6e
|
||||
eslint-plugin-import: 2.26.0_atoydicabq4zoeebkyg3dz56cy
|
||||
eslint-plugin-n: 15.3.0_eslint@8.27.0
|
||||
eslint-plugin-node: 11.1.0_eslint@8.27.0
|
||||
eslint-plugin-promise: 6.0.1_eslint@8.27.0
|
||||
eslint-plugin-unicorn: 43.0.2_eslint@8.27.0
|
||||
typescript: 4.8.4
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-node/0.3.6:
|
||||
resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==}
|
||||
dependencies:
|
||||
@ -2599,24 +2581,6 @@ packages:
|
||||
strip-indent: 3.0.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-vue/9.6.0_eslint@8.27.0:
|
||||
resolution: {integrity: sha512-zzySkJgVbFCylnG2+9MDF7N+2Rjze2y0bF8GyUNpFOnT8mCMfqqtLDJkHBuYu9N/psW1A6DVbQhPkP92E+qakA==}
|
||||
engines: {node: ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
|
||||
dependencies:
|
||||
eslint: 8.27.0
|
||||
eslint-utils: 3.0.0_eslint@8.27.0
|
||||
natural-compare: 1.4.0
|
||||
nth-check: 2.1.1
|
||||
postcss-selector-parser: 6.0.10
|
||||
semver: 7.3.8
|
||||
vue-eslint-parser: 9.1.0_eslint@8.27.0
|
||||
xml-name-validator: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-scope/5.1.1:
|
||||
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
@ -3720,17 +3684,17 @@ packages:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
dev: true
|
||||
|
||||
/listhen/0.3.5:
|
||||
resolution: {integrity: sha512-suyt79hNmCFeBIyftcLqLPfYiXeB795gSUWOJT7nspl2IvREY0Q9xvchLhekxvQ0KiOPvWoyALnc9Mxoelm0Pw==}
|
||||
/listhen/1.0.0:
|
||||
resolution: {integrity: sha512-frdf7TVqT/JSHzRjEuo/vWIgbBYzEuY3oeTq8Yv1XkQVTKDPs2M4yotXICqYZYj2QxbkqKssSo8Wa6QCtBnFhg==}
|
||||
dependencies:
|
||||
clipboardy: 3.0.0
|
||||
colorette: 2.0.19
|
||||
defu: 6.1.0
|
||||
defu: 6.1.1
|
||||
get-port-please: 2.6.1
|
||||
http-shutdown: 1.2.2
|
||||
ip-regex: 5.0.0
|
||||
node-forge: 1.3.1
|
||||
ufo: 0.8.6
|
||||
ufo: 1.0.0
|
||||
dev: false
|
||||
|
||||
/local-pkg/0.4.2:
|
||||
@ -4251,12 +4215,6 @@ packages:
|
||||
path-key: 4.0.0
|
||||
dev: true
|
||||
|
||||
/nth-check/2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
dev: true
|
||||
|
||||
/nwsapi/2.2.2:
|
||||
resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==}
|
||||
dev: true
|
||||
@ -4538,14 +4496,6 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/postcss-selector-parser/6.0.10:
|
||||
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
cssesc: 3.0.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/postcss/8.4.17:
|
||||
resolution: {integrity: sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@ -5492,24 +5442,6 @@ packages:
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/vue-eslint-parser/9.1.0_eslint@8.27.0:
|
||||
resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==}
|
||||
engines: {node: ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: '>=6.0.0'
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
eslint: 8.27.0
|
||||
eslint-scope: 7.1.1
|
||||
eslint-visitor-keys: 3.3.0
|
||||
espree: 9.4.0
|
||||
esquery: 1.4.0
|
||||
lodash: 4.17.21
|
||||
semver: 7.3.8
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vue/3.2.45:
|
||||
resolution: {integrity: sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==}
|
||||
dependencies:
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"@nuxtjs"
|
||||
"github>unjs/renovate-config"
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
|
||||
type Promisified<T> = Promise<Awaited<T>>
|
||||
|
||||
export function wrapToPromise<T> (val: T) {
|
||||
if (!val || typeof (val as any).then !== 'function') {
|
||||
return Promise.resolve(val) as Promisified<T>
|
||||
export function wrapToPromise<T> (value: T) {
|
||||
if (!value || typeof (value as any).then !== "function") {
|
||||
return Promise.resolve(value) as Promisified<T>;
|
||||
}
|
||||
return val as unknown as Promisified<T>
|
||||
return value as unknown as Promisified<T>;
|
||||
}
|
||||
|
||||
export function asyncCall<T extends (...args: any) => any>(fn: T, ...args: any[]): Promisified<ReturnType<T>> {
|
||||
export function asyncCall<T extends (...arguments_: any) => any>(function_: T, ...arguments_: any[]): Promisified<ReturnType<T>> {
|
||||
try {
|
||||
return wrapToPromise(fn(...args))
|
||||
} catch (err) {
|
||||
return Promise.reject(err)
|
||||
return wrapToPromise(function_(...arguments_));
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
export function isPrimitive (arg: any) {
|
||||
const type = typeof arg
|
||||
return arg === null || (type !== 'object' && type !== 'function')
|
||||
export function isPrimitive (argument: any) {
|
||||
const type = typeof argument;
|
||||
return argument === null || (type !== "object" && type !== "function");
|
||||
}
|
||||
|
||||
export function stringify (arg: any) {
|
||||
return isPrimitive(arg) ? (arg + '') : JSON.stringify(arg)
|
||||
export function stringify (argument: any) {
|
||||
return isPrimitive(argument) ? (argument + "") : JSON.stringify(argument);
|
||||
}
|
||||
|
||||
41
src/cli.ts
41
src/cli.ts
@ -1,35 +1,38 @@
|
||||
import { resolve } from 'path'
|
||||
import mri from 'mri'
|
||||
import { listen } from 'listhen'
|
||||
import { createStorage } from './storage'
|
||||
import { createStorageServer } from './server'
|
||||
import fsDriver from './drivers/fs'
|
||||
import { resolve } from "node:path";
|
||||
import mri from "mri";
|
||||
import { listen } from "listhen";
|
||||
import { createStorage } from "./storage";
|
||||
import { createStorageServer } from "./server";
|
||||
import fsDriver from "./drivers/fs";
|
||||
|
||||
async function main () {
|
||||
const args = mri(process.argv.splice(2))
|
||||
const arguments_ = mri(process.argv.splice(2));
|
||||
|
||||
if (args.help) {
|
||||
if (arguments_.help) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Usage: npx unstorage [rootDir]')
|
||||
process.exit(0)
|
||||
console.log("Usage: npx unstorage [rootDir]");
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const rootDir = resolve(arguments_._[0] || ".");
|
||||
|
||||
const storage = createStorage({
|
||||
driver: fsDriver({ base: rootDir })
|
||||
})
|
||||
});
|
||||
|
||||
const storageServer = createStorageServer(storage)
|
||||
const storageServer = createStorageServer(storage);
|
||||
|
||||
await listen(storageServer.handle, {
|
||||
name: 'Storage server',
|
||||
name: "Storage server",
|
||||
port: 8080
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
// eslint-disable-next-line unicorn/prefer-top-level-await
|
||||
main().catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
console.error(error);
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
34
src/index.ts
34
src/index.ts
@ -1,21 +1,21 @@
|
||||
export * from './storage'
|
||||
export * from './types'
|
||||
export * from './utils'
|
||||
export { defineDriver } from './drivers/utils'
|
||||
export * from "./storage";
|
||||
export * from "./types";
|
||||
export * from "./utils";
|
||||
export { defineDriver } from "./drivers/utils";
|
||||
|
||||
export const builtinDrivers = {
|
||||
cloudflareKVHTTP: 'unstorage/drivers/cloudflare-kv-http',
|
||||
cloudflareKVBinding: 'unstorage/drivers/cloudflare-kv-binding',
|
||||
'cloudflare-kv-http': 'unstorage/drivers/cloudflare-kv-http',
|
||||
'cloudflare-kv-binding': 'unstorage/drivers/cloudflare-kv-binding',
|
||||
fs: 'unstorage/drivers/fs',
|
||||
github: 'unstorage/drivers/github',
|
||||
http: 'unstorage/drivers/http',
|
||||
localStorage: 'unstorage/drivers/localstorage',
|
||||
localstorage: 'unstorage/drivers/localstorage',
|
||||
memory: 'unstorage/drivers/memory',
|
||||
overlay: 'unstorage/drivers/overlay',
|
||||
redis: 'unstorage/drivers/redis'
|
||||
}
|
||||
cloudflareKVHTTP: "unstorage/drivers/cloudflare-kv-http",
|
||||
cloudflareKVBinding: "unstorage/drivers/cloudflare-kv-binding",
|
||||
"cloudflare-kv-http": "unstorage/drivers/cloudflare-kv-http",
|
||||
"cloudflare-kv-binding": "unstorage/drivers/cloudflare-kv-binding",
|
||||
fs: "unstorage/drivers/fs",
|
||||
github: "unstorage/drivers/github",
|
||||
http: "unstorage/drivers/http",
|
||||
localStorage: "unstorage/drivers/localstorage",
|
||||
localstorage: "unstorage/drivers/localstorage",
|
||||
memory: "unstorage/drivers/memory",
|
||||
overlay: "unstorage/drivers/overlay",
|
||||
redis: "unstorage/drivers/redis"
|
||||
};
|
||||
|
||||
export type BuiltinDriverName = keyof typeof builtinDrivers
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { RequestListener } from 'http'
|
||||
import { createApp, createError, readBody, eventHandler, toNodeListener } from 'h3'
|
||||
import { Storage } from './types'
|
||||
import { stringify } from './_utils'
|
||||
import { RequestListener } from "node:http";
|
||||
import { createApp, createError, readBody, eventHandler, toNodeListener } from "h3";
|
||||
import { Storage } from "./types";
|
||||
import { stringify } from "./_utils";
|
||||
|
||||
export interface StorageServerOptions {}
|
||||
|
||||
@ -9,48 +9,48 @@ export interface StorageServer {
|
||||
handle: RequestListener
|
||||
}
|
||||
|
||||
export function createStorageServer (storage: Storage, _opts: StorageServerOptions = {}): StorageServer {
|
||||
const app = createApp()
|
||||
export function createStorageServer (storage: Storage, _options: StorageServerOptions = {}): StorageServer {
|
||||
const app = createApp();
|
||||
app.use(eventHandler(async (event) => {
|
||||
// GET => getItem
|
||||
if (event.req.method === 'GET') {
|
||||
const val = await storage.getItem(event.req.url!)
|
||||
if (!val) {
|
||||
const keys = await storage.getKeys(event.req.url)
|
||||
return keys.map(key => key.replace(/:/g, '/'))
|
||||
if (event.req.method === "GET") {
|
||||
const value = await storage.getItem(event.req.url!);
|
||||
if (!value) {
|
||||
const keys = await storage.getKeys(event.req.url);
|
||||
return keys.map(key => key.replace(/:/g, "/"));
|
||||
}
|
||||
return stringify(val)
|
||||
return stringify(value);
|
||||
}
|
||||
// HEAD => hasItem + meta (mtime)
|
||||
if (event.req.method === 'HEAD') {
|
||||
const _hasItem = await storage.hasItem(event.req.url!)
|
||||
event.res.statusCode = _hasItem ? 200 : 404
|
||||
if (event.req.method === "HEAD") {
|
||||
const _hasItem = await storage.hasItem(event.req.url!);
|
||||
event.res.statusCode = _hasItem ? 200 : 404;
|
||||
if (_hasItem) {
|
||||
const meta = await storage.getMeta(event.req.url!)
|
||||
const meta = await storage.getMeta(event.req.url!);
|
||||
if (meta.mtime) {
|
||||
event.res.setHeader('Last-Modified', new Date(meta.mtime).toUTCString())
|
||||
event.res.setHeader("Last-Modified", new Date(meta.mtime).toUTCString());
|
||||
}
|
||||
}
|
||||
return ''
|
||||
return "";
|
||||
}
|
||||
// PUT => setItem
|
||||
if (event.req.method === 'PUT') {
|
||||
const val = await readBody(event)
|
||||
await storage.setItem(event.req.url!, val)
|
||||
return 'OK'
|
||||
if (event.req.method === "PUT") {
|
||||
const value = await readBody(event);
|
||||
await storage.setItem(event.req.url!, value);
|
||||
return "OK";
|
||||
}
|
||||
// DELETE => removeItem
|
||||
if (event.req.method === 'DELETE') {
|
||||
await storage.removeItem(event.req.url!)
|
||||
return 'OK'
|
||||
if (event.req.method === "DELETE") {
|
||||
await storage.removeItem(event.req.url!);
|
||||
return "OK";
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 405,
|
||||
statusMessage: 'Method Not Allowed'
|
||||
})
|
||||
}))
|
||||
statusMessage: "Method Not Allowed"
|
||||
});
|
||||
}));
|
||||
|
||||
return {
|
||||
handle: toNodeListener(app)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
257
src/storage.ts
257
src/storage.ts
@ -1,8 +1,8 @@
|
||||
import destr from 'destr'
|
||||
import type { Storage, Driver, WatchCallback, Unwatch, StorageValue } from './types'
|
||||
import memory from './drivers/memory'
|
||||
import { asyncCall, stringify } from './_utils'
|
||||
import { normalizeKey, normalizeBaseKey } from './utils'
|
||||
import destr from "destr";
|
||||
import type { Storage, Driver, WatchCallback, Unwatch, StorageValue } from "./types";
|
||||
import memory from "./drivers/memory";
|
||||
import { asyncCall, stringify } from "./_utils";
|
||||
import { normalizeKey, normalizeBaseKey } from "./utils";
|
||||
|
||||
interface StorageCTX {
|
||||
mounts: Record<string, Driver>
|
||||
@ -16,250 +16,249 @@ export interface CreateStorageOptions {
|
||||
driver?: Driver
|
||||
}
|
||||
|
||||
export function createStorage (opts: CreateStorageOptions = {}): Storage {
|
||||
const ctx: StorageCTX = {
|
||||
mounts: { '': opts.driver || memory() },
|
||||
mountpoints: [''],
|
||||
export function createStorage (options: CreateStorageOptions = {}): Storage {
|
||||
const context: StorageCTX = {
|
||||
mounts: { "": options.driver || memory() },
|
||||
mountpoints: [""],
|
||||
watching: false,
|
||||
watchListeners: [],
|
||||
unwatch: {}
|
||||
}
|
||||
};
|
||||
|
||||
const getMount = (key: string) => {
|
||||
for (const base of ctx.mountpoints) {
|
||||
for (const base of context.mountpoints) {
|
||||
if (key.startsWith(base)) {
|
||||
return {
|
||||
relativeKey: key.substring(base.length),
|
||||
driver: ctx.mounts[base]
|
||||
}
|
||||
relativeKey: key.slice(base.length),
|
||||
driver: context.mounts[base]
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
relativeKey: key,
|
||||
driver: ctx.mounts['']
|
||||
}
|
||||
}
|
||||
driver: context.mounts[""]
|
||||
};
|
||||
};
|
||||
|
||||
const getMounts = (base: string, includeParent: boolean) => {
|
||||
return ctx.mountpoints
|
||||
return context.mountpoints
|
||||
.filter(mountpoint => (mountpoint.startsWith(base)) || (includeParent && base!.startsWith(mountpoint)))
|
||||
.map(mountpoint => ({
|
||||
relativeBase: base.length > mountpoint.length ? base!.substring(mountpoint.length) : undefined,
|
||||
relativeBase: base.length > mountpoint.length ? base!.slice(mountpoint.length) : undefined,
|
||||
mountpoint,
|
||||
driver: ctx.mounts[mountpoint]
|
||||
}))
|
||||
}
|
||||
driver: context.mounts[mountpoint]
|
||||
}));
|
||||
};
|
||||
|
||||
const onChange: WatchCallback = (event, key) => {
|
||||
if (!ctx.watching) { return }
|
||||
key = normalizeKey(key)
|
||||
for (const listener of ctx.watchListeners) {
|
||||
listener(event, key)
|
||||
if (!context.watching) { return; }
|
||||
key = normalizeKey(key);
|
||||
for (const listener of context.watchListeners) {
|
||||
listener(event, key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const startWatch = async () => {
|
||||
if (ctx.watching) { return }
|
||||
ctx.watching = true
|
||||
for (const mountpoint in ctx.mounts) {
|
||||
ctx.unwatch[mountpoint] = await watch(ctx.mounts[mountpoint], onChange, mountpoint)
|
||||
if (context.watching) { return; }
|
||||
context.watching = true;
|
||||
for (const mountpoint in context.mounts) {
|
||||
context.unwatch[mountpoint] = await watch(context.mounts[mountpoint], onChange, mountpoint);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const stopWatch = async () => {
|
||||
if (!ctx.watching) { return }
|
||||
for (const mountpoint in ctx.unwatch) {
|
||||
await ctx.unwatch[mountpoint]()
|
||||
if (!context.watching) { return; }
|
||||
for (const mountpoint in context.unwatch) {
|
||||
await context.unwatch[mountpoint]();
|
||||
}
|
||||
ctx.unwatch = {}
|
||||
ctx.watching = false
|
||||
}
|
||||
context.unwatch = {};
|
||||
context.watching = false;
|
||||
};
|
||||
|
||||
const storage: Storage = {
|
||||
// Item
|
||||
hasItem (key) {
|
||||
key = normalizeKey(key)
|
||||
const { relativeKey, driver } = getMount(key)
|
||||
return asyncCall(driver.hasItem, relativeKey)
|
||||
key = normalizeKey(key);
|
||||
const { relativeKey, driver } = getMount(key);
|
||||
return asyncCall(driver.hasItem, relativeKey);
|
||||
},
|
||||
getItem (key) {
|
||||
key = normalizeKey(key)
|
||||
const { relativeKey, driver } = getMount(key)
|
||||
return asyncCall(driver.getItem, relativeKey).then(val => destr(val))
|
||||
key = normalizeKey(key);
|
||||
const { relativeKey, driver } = getMount(key);
|
||||
return asyncCall(driver.getItem, relativeKey).then(value => destr(value));
|
||||
},
|
||||
async setItem (key, value) {
|
||||
if (value === undefined) {
|
||||
return storage.removeItem(key)
|
||||
return storage.removeItem(key);
|
||||
}
|
||||
key = normalizeKey(key)
|
||||
const { relativeKey, driver } = getMount(key)
|
||||
key = normalizeKey(key);
|
||||
const { relativeKey, driver } = getMount(key);
|
||||
if (!driver.setItem) {
|
||||
return // Readonly
|
||||
return; // Readonly
|
||||
}
|
||||
await asyncCall(driver.setItem, relativeKey, stringify(value))
|
||||
await asyncCall(driver.setItem, relativeKey, stringify(value));
|
||||
if (!driver.watch) {
|
||||
onChange('update', key)
|
||||
onChange("update", key);
|
||||
}
|
||||
},
|
||||
async removeItem (key, removeMeta = true) {
|
||||
key = normalizeKey(key)
|
||||
const { relativeKey, driver } = getMount(key)
|
||||
key = normalizeKey(key);
|
||||
const { relativeKey, driver } = getMount(key);
|
||||
if (!driver.removeItem) {
|
||||
return // Readonly
|
||||
return; // Readonly
|
||||
}
|
||||
await asyncCall(driver.removeItem, relativeKey)
|
||||
await asyncCall(driver.removeItem, relativeKey);
|
||||
if (removeMeta) {
|
||||
await asyncCall(driver.removeItem, relativeKey + '$')
|
||||
await asyncCall(driver.removeItem, relativeKey + "$");
|
||||
}
|
||||
if (!driver.watch) {
|
||||
onChange('remove', key)
|
||||
onChange("remove", key);
|
||||
}
|
||||
},
|
||||
// Meta
|
||||
async getMeta (key, nativeMetaOnly) {
|
||||
key = normalizeKey(key)
|
||||
const { relativeKey, driver } = getMount(key)
|
||||
const meta = Object.create(null)
|
||||
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));
|
||||
}
|
||||
if (!nativeMetaOnly) {
|
||||
const val = await asyncCall(driver.getItem, relativeKey + '$').then(val => destr(val))
|
||||
if (val && typeof val === 'object') {
|
||||
const value = await asyncCall(driver.getItem, relativeKey + "$").then(value_ => destr(value_));
|
||||
if (value && typeof value === "object") {
|
||||
// TODO: Support date by destr?
|
||||
if (typeof val.atime === 'string') { val.atime = new Date(val.atime) }
|
||||
if (typeof val.mtime === 'string') { val.mtime = new Date(val.mtime) }
|
||||
Object.assign(meta, val)
|
||||
if (typeof value.atime === "string") { value.atime = new Date(value.atime); }
|
||||
if (typeof value.mtime === "string") { value.mtime = new Date(value.mtime); }
|
||||
Object.assign(meta, value);
|
||||
}
|
||||
}
|
||||
return meta
|
||||
return meta;
|
||||
},
|
||||
setMeta (key: string, value: any) {
|
||||
return this.setItem(key + '$', value)
|
||||
return this.setItem(key + "$", value);
|
||||
},
|
||||
removeMeta (key: string) {
|
||||
return this.removeItem(key + '$')
|
||||
return this.removeItem(key + "$");
|
||||
},
|
||||
// Keys
|
||||
async getKeys (base) {
|
||||
base = normalizeBaseKey(base)
|
||||
const mounts = getMounts(base, true)
|
||||
let maskedMounts = []
|
||||
const allKeys = []
|
||||
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)
|
||||
const rawKeys = await asyncCall(mount.driver.getKeys, mount.relativeBase);
|
||||
const keys = rawKeys
|
||||
.map(key => mount.mountpoint + normalizeKey(key))
|
||||
.filter(key => !maskedMounts.find(p => key.startsWith(p)))
|
||||
allKeys.push(...keys)
|
||||
.filter(key => !maskedMounts.some(p => key.startsWith(p)));
|
||||
allKeys.push(...keys);
|
||||
|
||||
// When /mnt/foo is processed, any key in /mnt with /mnt/foo prefix should be masked
|
||||
// Using filter to improve performance. /mnt mask already covers /mnt/foo
|
||||
maskedMounts = [mount.mountpoint].concat(maskedMounts.filter(p => !p.startsWith(mount.mountpoint)))
|
||||
maskedMounts = [
|
||||
mount.mountpoint,
|
||||
...maskedMounts.filter(p => !p.startsWith(mount.mountpoint))
|
||||
];
|
||||
}
|
||||
return base
|
||||
? allKeys.filter(key => key.startsWith(base!) && !key.endsWith('$'))
|
||||
: allKeys.filter(key => !key.endsWith('$'))
|
||||
? allKeys.filter(key => key.startsWith(base!) && !key.endsWith("$"))
|
||||
: allKeys.filter(key => !key.endsWith("$"));
|
||||
},
|
||||
// Utils
|
||||
async clear (base) {
|
||||
base = normalizeBaseKey(base)
|
||||
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);
|
||||
}
|
||||
// Fallback to remove all keys if clear not implemented
|
||||
if (m.driver.removeItem) {
|
||||
const keys = await m.driver.getKeys()
|
||||
return Promise.all(keys.map(key => m.driver.removeItem!(key)))
|
||||
const keys = await m.driver.getKeys();
|
||||
return Promise.all(keys.map(key => m.driver.removeItem!(key)));
|
||||
}
|
||||
// Readonly
|
||||
}))
|
||||
}));
|
||||
},
|
||||
async dispose () {
|
||||
await Promise.all(Object.values(ctx.mounts).map(driver => dispose(driver)))
|
||||
await Promise.all(Object.values(context.mounts).map(driver => dispose(driver)));
|
||||
},
|
||||
async watch (callback) {
|
||||
await startWatch()
|
||||
ctx.watchListeners.push(callback)
|
||||
await startWatch();
|
||||
context.watchListeners.push(callback);
|
||||
return async () => {
|
||||
ctx.watchListeners = ctx.watchListeners.filter(listener => listener !== callback)
|
||||
if (ctx.watchListeners.length === 0) {
|
||||
await stopWatch()
|
||||
context.watchListeners = context.watchListeners.filter(listener => listener !== callback);
|
||||
if (context.watchListeners.length === 0) {
|
||||
await stopWatch();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
async unwatch () {
|
||||
ctx.watchListeners = []
|
||||
await stopWatch()
|
||||
context.watchListeners = [];
|
||||
await stopWatch();
|
||||
},
|
||||
// Mount
|
||||
mount (base, driver) {
|
||||
base = normalizeBaseKey(base)
|
||||
if (base && ctx.mounts[base]) {
|
||||
throw new Error(`already mounted at ${base}`)
|
||||
base = normalizeBaseKey(base);
|
||||
if (base && context.mounts[base]) {
|
||||
throw new Error(`already mounted at ${base}`);
|
||||
}
|
||||
if (base) {
|
||||
ctx.mountpoints.push(base)
|
||||
ctx.mountpoints.sort((a, b) => b.length - a.length)
|
||||
context.mountpoints.push(base);
|
||||
context.mountpoints.sort((a, b) => b.length - a.length);
|
||||
}
|
||||
ctx.mounts[base] = driver
|
||||
if (ctx.watching) {
|
||||
context.mounts[base] = driver;
|
||||
if (context.watching) {
|
||||
Promise.resolve(watch(driver, onChange, base))
|
||||
.then((unwatcher) => {
|
||||
ctx.unwatch[base] = unwatcher
|
||||
context.unwatch[base] = unwatcher;
|
||||
})
|
||||
.catch(console.error) // eslint-disable-line no-console
|
||||
.catch(console.error); // eslint-disable-line no-console
|
||||
}
|
||||
return storage
|
||||
return storage;
|
||||
},
|
||||
async unmount (base: string, _dispose = true) {
|
||||
base = normalizeBaseKey(base)
|
||||
if (!base /* root */ || !ctx.mounts[base]) {
|
||||
return
|
||||
base = normalizeBaseKey(base);
|
||||
if (!base /* root */ || !context.mounts[base]) {
|
||||
return;
|
||||
}
|
||||
if (ctx.watching && base in ctx.unwatch) {
|
||||
ctx.unwatch[base]()
|
||||
delete ctx.unwatch[base]
|
||||
if (context.watching && base in context.unwatch) {
|
||||
context.unwatch[base]();
|
||||
delete context.unwatch[base];
|
||||
}
|
||||
if (_dispose) {
|
||||
await dispose(ctx.mounts[base])
|
||||
await dispose(context.mounts[base]);
|
||||
}
|
||||
ctx.mountpoints = ctx.mountpoints.filter(key => key !== base)
|
||||
delete ctx.mounts[base]
|
||||
context.mountpoints = context.mountpoints.filter(key => key !== base);
|
||||
delete context.mounts[base];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return storage
|
||||
return storage;
|
||||
}
|
||||
|
||||
export type Snapshot<T=string> = Record<string, T>
|
||||
|
||||
export async function snapshot (storage: Storage, base: string): Promise<Snapshot<string>> {
|
||||
base = normalizeBaseKey(base)
|
||||
const keys = await storage.getKeys(base)
|
||||
const snapshot: any = {}
|
||||
base = normalizeBaseKey(base);
|
||||
const keys = await storage.getKeys(base);
|
||||
const snapshot: any = {};
|
||||
await Promise.all(keys.map(async (key) => {
|
||||
snapshot[key.substr(base.length)] = await storage.getItem(key)
|
||||
}))
|
||||
return snapshot
|
||||
snapshot[key.slice(base.length)] = await storage.getItem(key);
|
||||
}));
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
export async function restoreSnapshot (driver: Storage, snapshot: Snapshot<StorageValue>, base: string = '') {
|
||||
base = normalizeBaseKey(base)
|
||||
await Promise.all(Object.entries(snapshot).map(e => driver.setItem(base + e[0], e[1])))
|
||||
export async function restoreSnapshot (driver: Storage, snapshot: Snapshot<StorageValue>, base: string = "") {
|
||||
base = normalizeBaseKey(base);
|
||||
await Promise.all(Object.entries(snapshot).map(e => driver.setItem(base + e[0], e[1])));
|
||||
}
|
||||
|
||||
function watch (driver: Driver, onChange: WatchCallback, base: string) {
|
||||
if (driver.watch) {
|
||||
return driver.watch((event, key) => onChange(event, base + key))
|
||||
} else {
|
||||
return () => undefined
|
||||
}
|
||||
return driver.watch ? driver.watch((event, key) => onChange(event, base + key)) : () => {};
|
||||
}
|
||||
|
||||
async function dispose (driver: Driver) {
|
||||
if (typeof driver.dispose === 'function') {
|
||||
await asyncCall(driver.dispose)
|
||||
if (typeof driver.dispose === "function") {
|
||||
await asyncCall(driver.dispose);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export type StorageValue = null | string | String | number | Number | boolean | Boolean | object
|
||||
export type WatchEvent = 'update' | 'remove'
|
||||
export type WatchEvent = "update" | "remove"
|
||||
export type WatchCallback = (event: WatchEvent, key: string) => any
|
||||
|
||||
type MaybePromise<T> = T | Promise<T>
|
||||
|
||||
56
src/utils.ts
56
src/utils.ts
@ -1,50 +1,50 @@
|
||||
import type { Storage } from './types'
|
||||
import type { Storage } from "./types";
|
||||
|
||||
type StorageKeys = Array<keyof Storage>
|
||||
|
||||
const storageKeyProps: StorageKeys = [
|
||||
'hasItem',
|
||||
'getItem',
|
||||
'setItem',
|
||||
'removeItem',
|
||||
'getMeta',
|
||||
'setMeta',
|
||||
'removeMeta',
|
||||
'getKeys',
|
||||
'clear',
|
||||
'mount',
|
||||
'unmount'
|
||||
]
|
||||
const storageKeyProperties: StorageKeys = [
|
||||
"hasItem",
|
||||
"getItem",
|
||||
"setItem",
|
||||
"removeItem",
|
||||
"getMeta",
|
||||
"setMeta",
|
||||
"removeMeta",
|
||||
"getKeys",
|
||||
"clear",
|
||||
"mount",
|
||||
"unmount"
|
||||
];
|
||||
|
||||
export function prefixStorage (storage: Storage, base: string) {
|
||||
base = normalizeBaseKey(base)
|
||||
base = normalizeBaseKey(base);
|
||||
if (!base) {
|
||||
return storage
|
||||
return storage;
|
||||
}
|
||||
const nsStorage: Storage = { ...storage }
|
||||
for (const prop of storageKeyProps) {
|
||||
const nsStorage: Storage = { ...storage };
|
||||
for (const property of storageKeyProperties) {
|
||||
// @ts-ignore Better types?
|
||||
nsStorage[prop] = (key: string = '', ...args) => storage[prop](base + key, ...args)
|
||||
nsStorage[property] = (key: string = "", ...arguments_) => storage[property](base + key, ...arguments_);
|
||||
}
|
||||
nsStorage.getKeys = (key: string = '', ...args) =>
|
||||
nsStorage.getKeys = (key: string = "", ...arguments_) =>
|
||||
storage
|
||||
.getKeys(base + key, ...args)
|
||||
.getKeys(base + key, ...arguments_)
|
||||
// Remove Prefix
|
||||
.then(keys => keys.map(key => key.substr(base.length)))
|
||||
.then(keys => keys.map(key => key.slice(base.length)));
|
||||
|
||||
return nsStorage
|
||||
return nsStorage;
|
||||
}
|
||||
|
||||
export function normalizeKey (key?: string) {
|
||||
if (!key) { return '' }
|
||||
return key.replace(/[/\\]/g, ':').replace(/:+/g, ':').replace(/^:|:$/g, '')
|
||||
if (!key) { return ""; }
|
||||
return key.replace(/[/\\]/g, ":").replace(/:+/g, ":").replace(/^:|:$/g, "");
|
||||
}
|
||||
|
||||
export function joinKeys (...keys: string[]) {
|
||||
return normalizeKey(keys.join(':'))
|
||||
return normalizeKey(keys.join(":"));
|
||||
}
|
||||
|
||||
export function normalizeBaseKey (base?: string) {
|
||||
base = normalizeKey(base)
|
||||
return base ? (base + ':') : ''
|
||||
base = normalizeKey(base);
|
||||
return base ? (base + ":") : "";
|
||||
}
|
||||
|
||||
@ -1,32 +1,32 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { listen } from 'listhen'
|
||||
import { $fetch } from 'ohmyfetch'
|
||||
import { createStorage } from '../src'
|
||||
import { createStorageServer } from '../src/server'
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { listen } from "listhen";
|
||||
import { $fetch } from "ohmyfetch";
|
||||
import { createStorage } from "../src";
|
||||
import { createStorageServer } from "../src/server";
|
||||
|
||||
describe('server', () => {
|
||||
it('basic', async () => {
|
||||
const storage = createStorage()
|
||||
const storageServer = createStorageServer(storage)
|
||||
describe("server", () => {
|
||||
it("basic", async () => {
|
||||
const storage = createStorage();
|
||||
const storageServer = createStorageServer(storage);
|
||||
const { close, url: serverURL } = await listen(storageServer.handle, {
|
||||
port: { random: true }
|
||||
})
|
||||
});
|
||||
|
||||
const fetchStorage = (url: string, opts?: any) => $fetch(url, { baseURL: serverURL, ...opts })
|
||||
const fetchStorage = (url: string, options?: any) => $fetch(url, { baseURL: serverURL, ...options });
|
||||
|
||||
expect(await fetchStorage('foo', {})).toMatchObject([])
|
||||
expect(await fetchStorage("foo", {})).toMatchObject([]);
|
||||
|
||||
await storage.setItem('foo/bar', 'bar')
|
||||
await storage.setMeta('foo/bar', { mtime: new Date() })
|
||||
expect(await fetchStorage('foo/bar')).toBe('bar')
|
||||
await storage.setItem("foo/bar", "bar");
|
||||
await storage.setMeta("foo/bar", { mtime: new Date() });
|
||||
expect(await fetchStorage("foo/bar")).toBe("bar");
|
||||
|
||||
expect(await fetchStorage('foo/bar', { method: 'PUT', body: 'updated' })).toBe('OK')
|
||||
expect(await fetchStorage('foo/bar')).toBe('updated')
|
||||
expect(await fetchStorage('/')).toMatchObject(['foo/bar'])
|
||||
expect(await fetchStorage("foo/bar", { method: "PUT", body: "updated" })).toBe("OK");
|
||||
expect(await fetchStorage("foo/bar")).toBe("updated");
|
||||
expect(await fetchStorage("/")).toMatchObject(["foo/bar"]);
|
||||
|
||||
expect(await fetchStorage('foo/bar', { method: 'DELETE' })).toBe('OK')
|
||||
expect(await fetchStorage('foo/bar', {})).toMatchObject([])
|
||||
expect(await fetchStorage("foo/bar", { method: "DELETE" })).toBe("OK");
|
||||
expect(await fetchStorage("foo/bar", {})).toMatchObject([]);
|
||||
|
||||
await close()
|
||||
})
|
||||
})
|
||||
await close();
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,107 +1,107 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { createStorage, snapshot, restoreSnapshot, prefixStorage } from '../src'
|
||||
import memory from '../src/drivers/memory'
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { createStorage, snapshot, restoreSnapshot, prefixStorage } from "../src";
|
||||
import memory from "../src/drivers/memory";
|
||||
|
||||
const data = {
|
||||
'etc:conf': 'test',
|
||||
'data:foo': 123
|
||||
}
|
||||
"etc:conf": "test",
|
||||
"data:foo": 123
|
||||
};
|
||||
|
||||
describe('storage', () => {
|
||||
it('mount/unmount', async () => {
|
||||
const storage = createStorage().mount('/mnt', memory())
|
||||
await restoreSnapshot(storage, data, 'mnt')
|
||||
expect(await snapshot(storage, '/mnt')).toMatchObject(data)
|
||||
})
|
||||
describe("storage", () => {
|
||||
it("mount/unmount", async () => {
|
||||
const storage = createStorage().mount("/mnt", memory());
|
||||
await restoreSnapshot(storage, data, "mnt");
|
||||
expect(await snapshot(storage, "/mnt")).toMatchObject(data);
|
||||
});
|
||||
|
||||
it('snapshot', async () => {
|
||||
const storage = createStorage()
|
||||
await restoreSnapshot(storage, data)
|
||||
expect(await snapshot(storage, '')).toMatchObject(data)
|
||||
})
|
||||
it("snapshot", async () => {
|
||||
const storage = createStorage();
|
||||
await restoreSnapshot(storage, data);
|
||||
expect(await snapshot(storage, "")).toMatchObject(data);
|
||||
});
|
||||
|
||||
it('watch', async () => {
|
||||
const onChange = vi.fn()
|
||||
const storage = createStorage().mount('/mnt', memory())
|
||||
await storage.watch(onChange)
|
||||
await restoreSnapshot(storage, data, 'mnt')
|
||||
expect(onChange).toHaveBeenCalledWith('update', 'mnt:etc:conf')
|
||||
expect(onChange).toHaveBeenCalledWith('update', 'mnt:data:foo')
|
||||
expect(onChange).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
it("watch", async () => {
|
||||
const onChange = vi.fn();
|
||||
const storage = createStorage().mount("/mnt", memory());
|
||||
await storage.watch(onChange);
|
||||
await restoreSnapshot(storage, data, "mnt");
|
||||
expect(onChange).toHaveBeenCalledWith("update", "mnt:etc:conf");
|
||||
expect(onChange).toHaveBeenCalledWith("update", "mnt:data:foo");
|
||||
expect(onChange).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('unwatch return', async () => {
|
||||
const onChange = vi.fn()
|
||||
const storage = createStorage().mount('/mnt', memory())
|
||||
const unwatch = await storage.watch(onChange)
|
||||
await storage.setItem('mnt:data:foo', 42)
|
||||
await unwatch()
|
||||
await storage.setItem('mnt:data:foo', 41)
|
||||
expect(onChange).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
it("unwatch return", async () => {
|
||||
const onChange = vi.fn();
|
||||
const storage = createStorage().mount("/mnt", memory());
|
||||
const unwatch = await storage.watch(onChange);
|
||||
await storage.setItem("mnt:data:foo", 42);
|
||||
await unwatch();
|
||||
await storage.setItem("mnt:data:foo", 41);
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('unwatch all', async () => {
|
||||
const onChange = vi.fn()
|
||||
const storage = createStorage().mount('/mnt', memory())
|
||||
await storage.watch(onChange)
|
||||
await storage.setItem('mnt:data:foo', 42)
|
||||
await storage.unwatch()
|
||||
await storage.setItem('mnt:data:foo', 41)
|
||||
expect(onChange).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
it("unwatch all", async () => {
|
||||
const onChange = vi.fn();
|
||||
const storage = createStorage().mount("/mnt", memory());
|
||||
await storage.watch(onChange);
|
||||
await storage.setItem("mnt:data:foo", 42);
|
||||
await storage.unwatch();
|
||||
await storage.setItem("mnt:data:foo", 41);
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('mount overides', async () => {
|
||||
const mainStorage = memory()
|
||||
const storage = createStorage({ driver: mainStorage })
|
||||
await storage.setItem('/mnt/test.txt', 'v1')
|
||||
await storage.setItem('/mnt/test.base.txt', 'v1')
|
||||
it("mount overides", async () => {
|
||||
const mainStorage = memory();
|
||||
const storage = createStorage({ driver: mainStorage });
|
||||
await storage.setItem("/mnt/test.txt", "v1");
|
||||
await storage.setItem("/mnt/test.base.txt", "v1");
|
||||
|
||||
const initialKeys = await storage.getKeys()
|
||||
const initialKeys = await storage.getKeys();
|
||||
expect(initialKeys).toMatchInlineSnapshot(`
|
||||
[
|
||||
"mnt:test.txt",
|
||||
"mnt:test.base.txt",
|
||||
]
|
||||
`)
|
||||
`);
|
||||
|
||||
storage.mount('/mnt', memory())
|
||||
await storage.setItem('/mnt/test.txt', 'v2')
|
||||
storage.mount("/mnt", memory());
|
||||
await storage.setItem("/mnt/test.txt", "v2");
|
||||
|
||||
await storage.setItem('/mnt/foo/test.txt', 'v3')
|
||||
storage.mount('/mnt/foo', memory())
|
||||
expect(await storage.getItem('/mnt/foo/test.txt')).toBe(null)
|
||||
await storage.setItem("/mnt/foo/test.txt", "v3");
|
||||
storage.mount("/mnt/foo", memory());
|
||||
expect(await storage.getItem("/mnt/foo/test.txt")).toBe(null);
|
||||
|
||||
expect(await storage.getItem('/mnt/test.txt')).toBe('v2')
|
||||
expect(await storage.getItem("/mnt/test.txt")).toBe("v2");
|
||||
expect(await storage.getKeys()).toMatchInlineSnapshot(`
|
||||
[
|
||||
"mnt:test.txt",
|
||||
]
|
||||
`)
|
||||
`);
|
||||
|
||||
await storage.clear('/mnt')
|
||||
await storage.unmount('/mnt')
|
||||
expect(await storage.getKeys()).toMatchObject(initialKeys)
|
||||
expect(await storage.getItem('/mnt/test.txt')).toBe('v1')
|
||||
})
|
||||
})
|
||||
await storage.clear("/mnt");
|
||||
await storage.unmount("/mnt");
|
||||
expect(await storage.getKeys()).toMatchObject(initialKeys);
|
||||
expect(await storage.getItem("/mnt/test.txt")).toBe("v1");
|
||||
});
|
||||
});
|
||||
|
||||
describe('utils', () => {
|
||||
it('prefixStorage', async () => {
|
||||
const storage = createStorage()
|
||||
const pStorage = prefixStorage(storage, 'foo')
|
||||
await pStorage.setItem('x', 'bar')
|
||||
await pStorage.setItem('y', 'baz')
|
||||
expect(await storage.getItem('foo:x')).toBe('bar')
|
||||
expect(await pStorage.getItem('x')).toBe('bar')
|
||||
expect(await pStorage.getKeys()).toStrictEqual(['x', 'y'])
|
||||
describe("utils", () => {
|
||||
it("prefixStorage", async () => {
|
||||
const storage = createStorage();
|
||||
const pStorage = prefixStorage(storage, "foo");
|
||||
await pStorage.setItem("x", "bar");
|
||||
await pStorage.setItem("y", "baz");
|
||||
expect(await storage.getItem("foo:x")).toBe("bar");
|
||||
expect(await pStorage.getItem("x")).toBe("bar");
|
||||
expect(await pStorage.getKeys()).toStrictEqual(["x", "y"]);
|
||||
|
||||
// Higher order storage
|
||||
const secondStorage = createStorage()
|
||||
secondStorage.mount('/mnt', storage)
|
||||
const mntStorage = prefixStorage(secondStorage, 'mnt')
|
||||
const secondStorage = createStorage();
|
||||
secondStorage.mount("/mnt", storage);
|
||||
const mntStorage = prefixStorage(secondStorage, "mnt");
|
||||
|
||||
expect(await mntStorage.getKeys()).toStrictEqual(['foo:x', 'foo:y'])
|
||||
expect(await mntStorage.getKeys()).toStrictEqual(["foo:x", "foo:y"]);
|
||||
// Get keys from sub-storage
|
||||
expect(await mntStorage.getKeys('foo')).toStrictEqual(['foo:x', 'foo:y'])
|
||||
})
|
||||
})
|
||||
expect(await mntStorage.getKeys("foo")).toStrictEqual(["foo:x", "foo:y"]);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user