refactor: update repository

This commit is contained in:
Pooya Parsa 2022-11-15 02:16:38 +01:00
parent 5ac62d7b45
commit ae352daa68
17 changed files with 419 additions and 480 deletions

View File

@ -1,5 +1,9 @@
{ {
"extends": [ "extends": [
"@nuxtjs/eslint-config-typescript" "eslint-config-unjs"
] ],
"rules": {
"unicorn/no-null": 0,
"unicorn/prevent-abbreviations": 0
}
} }

View File

@ -1,6 +1,6 @@
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -113,7 +113,7 @@ await storage.hasItem('foo:bar')
### `storage.getItem(key)` ### `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 ```js
await storage.getItem('foo:bar') await storage.getItem('foo:bar')

View File

@ -1,4 +1,4 @@
import { defineBuildConfig } from 'unbuild' import { defineBuildConfig } from "unbuild";
export default defineBuildConfig({ export default defineBuildConfig({
declaration: true, declaration: true,
@ -6,9 +6,9 @@ export default defineBuildConfig({
emitCJS: true emitCJS: true
}, },
entries: [ entries: [
'src/index', "src/index",
'src/server', "src/server",
{ input: 'src/drivers/', outDir: 'dist/drivers', format: 'esm' }, { input: "src/drivers/", outDir: "dist/drivers", format: "esm" },
{ input: 'src/drivers/', outDir: 'dist/drivers', format: 'cjs', ext: 'cjs', declaration: false } { input: "src/drivers/", outDir: "dist/drivers", format: "cjs", ext: "cjs", declaration: false }
] ]
}) });

View File

@ -1,27 +1,28 @@
import { resolve } from 'path' import { resolve } from "node:path";
import { defineConfig } from 'vite' import { defineConfig } from "vite";
import vue from '@vitejs/plugin-vue' import vue from "@vitejs/plugin-vue";
import { createStorage } from '../src' import { createStorage } from "../src";
import { createStorageServer } from '../src/server' import { createStorageServer } from "../src/server";
import fsdriver from '../src/drivers/fs' import fsdriver from "../src/drivers/fs";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
resolve: { resolve: {
alias: { alias: {
'node-fetch': 'node-fetch/browser' "node-fetch": "node-fetch/browser"
} }
}, },
plugins: [ plugins: [
vue(), vue(),
{ {
name: 'app', name: "app",
configureServer (server) { configureServer (server) {
const storage = createStorage() const storage = createStorage();
const storageServer = createStorageServer(storage) const storageServer = createStorageServer(storage);
storage.mount('/src', fsdriver({ base: resolve(__dirname, '..') })) // eslint-disable-next-line unicorn/prefer-module
server.middlewares.use('/storage', storageServer.handle) storage.mount("/src", fsdriver({ base: resolve(__dirname, "..") }));
server.middlewares.use("/storage", storageServer.handle);
} }
} }
] ]
}) });

View File

@ -13,7 +13,8 @@
}, },
".": { ".": {
"import": "./dist/index.mjs", "import": "./dist/index.mjs",
"require": "./dist/index.cjs" "require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}, },
"./server": { "./server": {
"import": "./dist/server.mjs", "import": "./dist/server.mjs",
@ -44,7 +45,7 @@
"destr": "^1.2.1", "destr": "^1.2.1",
"h3": "^0.8.6", "h3": "^0.8.6",
"ioredis": "^5.2.4", "ioredis": "^5.2.4",
"listhen": "^0.3.5", "listhen": "^1.0.0",
"mkdir": "^0.0.2", "mkdir": "^0.0.2",
"mri": "^1.2.0", "mri": "^1.2.0",
"ohmyfetch": "^0.4.21", "ohmyfetch": "^0.4.21",
@ -53,8 +54,6 @@
}, },
"devDependencies": { "devDependencies": {
"@cloudflare/workers-types": "^3.18.0", "@cloudflare/workers-types": "^3.18.0",
"@nuxtjs/eslint-config-typescript": "^11.0.0",
"@types/ioredis": "^4.28.10",
"@types/jsdom": "^20.0.1", "@types/jsdom": "^20.0.1",
"@types/mri": "^1.1.1", "@types/mri": "^1.1.1",
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
@ -66,6 +65,7 @@
"changelogen": "^0.4.0", "changelogen": "^0.4.0",
"doctoc": "^2.2.1", "doctoc": "^2.2.1",
"eslint": "^8.27.0", "eslint": "^8.27.0",
"eslint-config-unjs": "^0.0.2",
"jiti": "^1.16.0", "jiti": "^1.16.0",
"jsdom": "^20.0.2", "jsdom": "^20.0.2",
"mkdist": "latest", "mkdist": "latest",

158
pnpm-lock.yaml generated
View File

@ -2,8 +2,6 @@ lockfileVersion: 5.4
specifiers: specifiers:
'@cloudflare/workers-types': ^3.18.0 '@cloudflare/workers-types': ^3.18.0
'@nuxtjs/eslint-config-typescript': ^11.0.0
'@types/ioredis': ^4.28.10
'@types/jsdom': ^20.0.1 '@types/jsdom': ^20.0.1
'@types/mri': ^1.1.1 '@types/mri': ^1.1.1
'@types/node': ^18.11.9 '@types/node': ^18.11.9
@ -18,11 +16,12 @@ specifiers:
destr: ^1.2.1 destr: ^1.2.1
doctoc: ^2.2.1 doctoc: ^2.2.1
eslint: ^8.27.0 eslint: ^8.27.0
eslint-config-unjs: ^0.0.2
h3: ^0.8.6 h3: ^0.8.6
ioredis: ^5.2.4 ioredis: ^5.2.4
jiti: ^1.16.0 jiti: ^1.16.0
jsdom: ^20.0.2 jsdom: ^20.0.2
listhen: ^0.3.5 listhen: ^1.0.0
mkdir: ^0.0.2 mkdir: ^0.0.2
mkdist: latest mkdist: latest
monaco-editor: ^0.34.1 monaco-editor: ^0.34.1
@ -44,7 +43,7 @@ dependencies:
destr: 1.2.1 destr: 1.2.1
h3: 0.8.6 h3: 0.8.6
ioredis: 5.2.4 ioredis: 5.2.4
listhen: 0.3.5 listhen: 1.0.0
mkdir: 0.0.2 mkdir: 0.0.2
mri: 1.2.0 mri: 1.2.0
ohmyfetch: 0.4.21 ohmyfetch: 0.4.21
@ -53,8 +52,6 @@ dependencies:
devDependencies: devDependencies:
'@cloudflare/workers-types': 3.18.0 '@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/jsdom': 20.0.1
'@types/mri': 1.1.1 '@types/mri': 1.1.1
'@types/node': 18.11.9 '@types/node': 18.11.9
@ -66,6 +63,7 @@ devDependencies:
changelogen: 0.4.0 changelogen: 0.4.0
doctoc: 2.2.1 doctoc: 2.2.1
eslint: 8.27.0 eslint: 8.27.0
eslint-config-unjs: 0.0.2_rmayb2veg2btbq6mbmnyivgasy
jiti: 1.16.0 jiti: 1.16.0
jsdom: 20.0.2 jsdom: 20.0.2
mkdist: 0.4.0_typescript@4.8.4 mkdist: 0.4.0_typescript@4.8.4
@ -459,43 +457,6 @@ packages:
fastq: 1.13.0 fastq: 1.13.0
dev: true 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: /@open-draft/until/1.0.3:
resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==} resolution: {integrity: sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==}
dev: true dev: true
@ -659,12 +620,6 @@ packages:
resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
dev: true 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: /@types/istanbul-lib-coverage/2.0.4:
resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
dev: true dev: true
@ -1143,10 +1098,6 @@ packages:
readable-stream: 3.6.0 readable-stream: 3.6.0
dev: true dev: true
/boolbase/1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
dev: true
/brace-expansion/1.1.11: /brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies: dependencies:
@ -1476,12 +1427,6 @@ packages:
shebang-command: 2.0.0 shebang-command: 2.0.0
which: 2.0.2 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: /cssom/0.3.8:
resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
dev: true dev: true
@ -1584,6 +1529,11 @@ packages:
/defu/6.1.0: /defu/6.1.0:
resolution: {integrity: sha512-pOFYRTIhoKujrmbTRhcW5lYQLBXw/dlTwfI8IguF1QCDJOcJzNH1w+YFjxqy6BAuJrClTy6MUE8q+oKJ2FLsIw==} 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: /delayed-stream/1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
@ -2412,6 +2362,15 @@ packages:
source-map: 0.6.1 source-map: 0.6.1
dev: true 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: /eslint-config-standard/17.0.0_5p2jx74osvs7etmizpb2fndoma:
resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==} resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==}
peerDependencies: peerDependencies:
@ -2426,6 +2385,29 @@ packages:
eslint-plugin-promise: 6.0.1_eslint@8.27.0 eslint-plugin-promise: 6.0.1_eslint@8.27.0
dev: true 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: /eslint-import-resolver-node/0.3.6:
resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==}
dependencies: dependencies:
@ -2599,24 +2581,6 @@ packages:
strip-indent: 3.0.0 strip-indent: 3.0.0
dev: true 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: /eslint-scope/5.1.1:
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
@ -3720,17 +3684,17 @@ packages:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: true dev: true
/listhen/0.3.5: /listhen/1.0.0:
resolution: {integrity: sha512-suyt79hNmCFeBIyftcLqLPfYiXeB795gSUWOJT7nspl2IvREY0Q9xvchLhekxvQ0KiOPvWoyALnc9Mxoelm0Pw==} resolution: {integrity: sha512-frdf7TVqT/JSHzRjEuo/vWIgbBYzEuY3oeTq8Yv1XkQVTKDPs2M4yotXICqYZYj2QxbkqKssSo8Wa6QCtBnFhg==}
dependencies: dependencies:
clipboardy: 3.0.0 clipboardy: 3.0.0
colorette: 2.0.19 colorette: 2.0.19
defu: 6.1.0 defu: 6.1.1
get-port-please: 2.6.1 get-port-please: 2.6.1
http-shutdown: 1.2.2 http-shutdown: 1.2.2
ip-regex: 5.0.0 ip-regex: 5.0.0
node-forge: 1.3.1 node-forge: 1.3.1
ufo: 0.8.6 ufo: 1.0.0
dev: false dev: false
/local-pkg/0.4.2: /local-pkg/0.4.2:
@ -4251,12 +4215,6 @@ packages:
path-key: 4.0.0 path-key: 4.0.0
dev: true 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: /nwsapi/2.2.2:
resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==} resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==}
dev: true dev: true
@ -4538,14 +4496,6 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true 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: /postcss/8.4.17:
resolution: {integrity: sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==} resolution: {integrity: sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
@ -5492,24 +5442,6 @@ packages:
- terser - terser
dev: true 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: /vue/3.2.45:
resolution: {integrity: sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==} resolution: {integrity: sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==}
dependencies: dependencies:

View File

@ -1,5 +1,5 @@
{ {
"extends": [ "extends": [
"@nuxtjs" "github>unjs/renovate-config"
] ]
} }

View File

@ -1,26 +1,26 @@
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T; type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type Promisified<T> = Promise<Awaited<T>> type Promisified<T> = Promise<Awaited<T>>
export function wrapToPromise<T> (val: T) { export function wrapToPromise<T> (value: T) {
if (!val || typeof (val as any).then !== 'function') { if (!value || typeof (value as any).then !== "function") {
return Promise.resolve(val) as Promisified<T> 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 { try {
return wrapToPromise(fn(...args)) return wrapToPromise(function_(...arguments_));
} catch (err) { } catch (error) {
return Promise.reject(err) return Promise.reject(error);
} }
} }
export function isPrimitive (arg: any) { export function isPrimitive (argument: any) {
const type = typeof arg const type = typeof argument;
return arg === null || (type !== 'object' && type !== 'function') return argument === null || (type !== "object" && type !== "function");
} }
export function stringify (arg: any) { export function stringify (argument: any) {
return isPrimitive(arg) ? (arg + '') : JSON.stringify(arg) return isPrimitive(argument) ? (argument + "") : JSON.stringify(argument);
} }

View File

@ -1,35 +1,38 @@
import { resolve } from 'path' import { resolve } from "node:path";
import mri from 'mri' import mri from "mri";
import { listen } from 'listhen' import { listen } from "listhen";
import { createStorage } from './storage' import { createStorage } from "./storage";
import { createStorageServer } from './server' import { createStorageServer } from "./server";
import fsDriver from './drivers/fs' import fsDriver from "./drivers/fs";
async function main () { 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 // eslint-disable-next-line no-console
console.log('Usage: npx unstorage [rootDir]') console.log("Usage: npx unstorage [rootDir]");
process.exit(0) // eslint-disable-next-line unicorn/no-process-exit
process.exit(0);
} }
const rootDir = resolve(args._[0] || '.') const rootDir = resolve(arguments_._[0] || ".");
const storage = createStorage({ const storage = createStorage({
driver: fsDriver({ base: rootDir }) driver: fsDriver({ base: rootDir })
}) });
const storageServer = createStorageServer(storage) const storageServer = createStorageServer(storage);
await listen(storageServer.handle, { await listen(storageServer.handle, {
name: 'Storage server', name: "Storage server",
port: 8080 port: 8080
}) });
} }
main().catch((err) => { // eslint-disable-next-line unicorn/prefer-top-level-await
main().catch((error) => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(err) console.error(error);
process.exit(1) // eslint-disable-next-line unicorn/no-process-exit
}) process.exit(1);
});

View File

@ -1,21 +1,21 @@
export * from './storage' export * from "./storage";
export * from './types' export * from "./types";
export * from './utils' export * from "./utils";
export { defineDriver } from './drivers/utils' export { defineDriver } from "./drivers/utils";
export const builtinDrivers = { export const builtinDrivers = {
cloudflareKVHTTP: 'unstorage/drivers/cloudflare-kv-http', cloudflareKVHTTP: "unstorage/drivers/cloudflare-kv-http",
cloudflareKVBinding: 'unstorage/drivers/cloudflare-kv-binding', cloudflareKVBinding: "unstorage/drivers/cloudflare-kv-binding",
'cloudflare-kv-http': 'unstorage/drivers/cloudflare-kv-http', "cloudflare-kv-http": "unstorage/drivers/cloudflare-kv-http",
'cloudflare-kv-binding': 'unstorage/drivers/cloudflare-kv-binding', "cloudflare-kv-binding": "unstorage/drivers/cloudflare-kv-binding",
fs: 'unstorage/drivers/fs', fs: "unstorage/drivers/fs",
github: 'unstorage/drivers/github', github: "unstorage/drivers/github",
http: 'unstorage/drivers/http', http: "unstorage/drivers/http",
localStorage: 'unstorage/drivers/localstorage', localStorage: "unstorage/drivers/localstorage",
localstorage: 'unstorage/drivers/localstorage', localstorage: "unstorage/drivers/localstorage",
memory: 'unstorage/drivers/memory', memory: "unstorage/drivers/memory",
overlay: 'unstorage/drivers/overlay', overlay: "unstorage/drivers/overlay",
redis: 'unstorage/drivers/redis' redis: "unstorage/drivers/redis"
} };
export type BuiltinDriverName = keyof typeof builtinDrivers export type BuiltinDriverName = keyof typeof builtinDrivers

View File

@ -1,7 +1,7 @@
import { RequestListener } from 'http' import { RequestListener } from "node:http";
import { createApp, createError, readBody, eventHandler, toNodeListener } from 'h3' import { createApp, createError, readBody, eventHandler, toNodeListener } from "h3";
import { Storage } from './types' import { Storage } from "./types";
import { stringify } from './_utils' import { stringify } from "./_utils";
export interface StorageServerOptions {} export interface StorageServerOptions {}
@ -9,48 +9,48 @@ export interface StorageServer {
handle: RequestListener handle: RequestListener
} }
export function createStorageServer (storage: Storage, _opts: StorageServerOptions = {}): StorageServer { export function createStorageServer (storage: Storage, _options: StorageServerOptions = {}): StorageServer {
const app = createApp() const app = createApp();
app.use(eventHandler(async (event) => { app.use(eventHandler(async (event) => {
// GET => getItem // GET => getItem
if (event.req.method === 'GET') { if (event.req.method === "GET") {
const val = await storage.getItem(event.req.url!) const value = await storage.getItem(event.req.url!);
if (!val) { if (!value) {
const keys = await storage.getKeys(event.req.url) const keys = await storage.getKeys(event.req.url);
return keys.map(key => key.replace(/:/g, '/')) return keys.map(key => key.replace(/:/g, "/"));
} }
return stringify(val) return stringify(value);
} }
// HEAD => hasItem + meta (mtime) // HEAD => hasItem + meta (mtime)
if (event.req.method === 'HEAD') { if (event.req.method === "HEAD") {
const _hasItem = await storage.hasItem(event.req.url!) const _hasItem = await storage.hasItem(event.req.url!);
event.res.statusCode = _hasItem ? 200 : 404 event.res.statusCode = _hasItem ? 200 : 404;
if (_hasItem) { if (_hasItem) {
const meta = await storage.getMeta(event.req.url!) const meta = await storage.getMeta(event.req.url!);
if (meta.mtime) { 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 // PUT => setItem
if (event.req.method === 'PUT') { if (event.req.method === "PUT") {
const val = await readBody(event) const value = await readBody(event);
await storage.setItem(event.req.url!, val) await storage.setItem(event.req.url!, value);
return 'OK' return "OK";
} }
// DELETE => removeItem // DELETE => removeItem
if (event.req.method === 'DELETE') { if (event.req.method === "DELETE") {
await storage.removeItem(event.req.url!) await storage.removeItem(event.req.url!);
return 'OK' return "OK";
} }
throw createError({ throw createError({
statusCode: 405, statusCode: 405,
statusMessage: 'Method Not Allowed' statusMessage: "Method Not Allowed"
}) });
})) }));
return { return {
handle: toNodeListener(app) handle: toNodeListener(app)
} };
} }

View File

@ -1,8 +1,8 @@
import destr from 'destr' import destr from "destr";
import type { Storage, Driver, WatchCallback, Unwatch, StorageValue } from './types' import type { Storage, Driver, WatchCallback, Unwatch, StorageValue } from "./types";
import memory from './drivers/memory' import memory from "./drivers/memory";
import { asyncCall, stringify } from './_utils' import { asyncCall, stringify } from "./_utils";
import { normalizeKey, normalizeBaseKey } from './utils' import { normalizeKey, normalizeBaseKey } from "./utils";
interface StorageCTX { interface StorageCTX {
mounts: Record<string, Driver> mounts: Record<string, Driver>
@ -16,250 +16,249 @@ export interface CreateStorageOptions {
driver?: Driver driver?: Driver
} }
export function createStorage (opts: CreateStorageOptions = {}): Storage { export function createStorage (options: CreateStorageOptions = {}): Storage {
const ctx: StorageCTX = { const context: StorageCTX = {
mounts: { '': opts.driver || memory() }, mounts: { "": options.driver || memory() },
mountpoints: [''], mountpoints: [""],
watching: false, watching: false,
watchListeners: [], watchListeners: [],
unwatch: {} unwatch: {}
} };
const getMount = (key: string) => { const getMount = (key: string) => {
for (const base of ctx.mountpoints) { for (const base of context.mountpoints) {
if (key.startsWith(base)) { if (key.startsWith(base)) {
return { return {
relativeKey: key.substring(base.length), relativeKey: key.slice(base.length),
driver: ctx.mounts[base] driver: context.mounts[base]
} };
} }
} }
return { return {
relativeKey: key, relativeKey: key,
driver: ctx.mounts[''] driver: context.mounts[""]
} };
} };
const getMounts = (base: string, includeParent: boolean) => { const getMounts = (base: string, includeParent: boolean) => {
return ctx.mountpoints return context.mountpoints
.filter(mountpoint => (mountpoint.startsWith(base)) || (includeParent && base!.startsWith(mountpoint))) .filter(mountpoint => (mountpoint.startsWith(base)) || (includeParent && base!.startsWith(mountpoint)))
.map(mountpoint => ({ .map(mountpoint => ({
relativeBase: base.length > mountpoint.length ? base!.substring(mountpoint.length) : undefined, relativeBase: base.length > mountpoint.length ? base!.slice(mountpoint.length) : undefined,
mountpoint, mountpoint,
driver: ctx.mounts[mountpoint] driver: context.mounts[mountpoint]
})) }));
} };
const onChange: WatchCallback = (event, key) => { const onChange: WatchCallback = (event, key) => {
if (!ctx.watching) { return } if (!context.watching) { return; }
key = normalizeKey(key) key = normalizeKey(key);
for (const listener of ctx.watchListeners) { for (const listener of context.watchListeners) {
listener(event, key) listener(event, key);
}
} }
};
const startWatch = async () => { const startWatch = async () => {
if (ctx.watching) { return } if (context.watching) { return; }
ctx.watching = true context.watching = true;
for (const mountpoint in ctx.mounts) { for (const mountpoint in context.mounts) {
ctx.unwatch[mountpoint] = await watch(ctx.mounts[mountpoint], onChange, mountpoint) context.unwatch[mountpoint] = await watch(context.mounts[mountpoint], onChange, mountpoint);
}
} }
};
const stopWatch = async () => { const stopWatch = async () => {
if (!ctx.watching) { return } if (!context.watching) { return; }
for (const mountpoint in ctx.unwatch) { for (const mountpoint in context.unwatch) {
await ctx.unwatch[mountpoint]() await context.unwatch[mountpoint]();
}
ctx.unwatch = {}
ctx.watching = false
} }
context.unwatch = {};
context.watching = false;
};
const storage: Storage = { const storage: Storage = {
// Item // Item
hasItem (key) { hasItem (key) {
key = normalizeKey(key) key = normalizeKey(key);
const { relativeKey, driver } = getMount(key) const { relativeKey, driver } = getMount(key);
return asyncCall(driver.hasItem, relativeKey) return asyncCall(driver.hasItem, relativeKey);
}, },
getItem (key) { getItem (key) {
key = normalizeKey(key) key = normalizeKey(key);
const { relativeKey, driver } = getMount(key) const { relativeKey, driver } = getMount(key);
return asyncCall(driver.getItem, relativeKey).then(val => destr(val)) return asyncCall(driver.getItem, relativeKey).then(value => destr(value));
}, },
async setItem (key, value) { async setItem (key, value) {
if (value === undefined) { if (value === undefined) {
return storage.removeItem(key) return storage.removeItem(key);
} }
key = normalizeKey(key) key = normalizeKey(key);
const { relativeKey, driver } = getMount(key) const { relativeKey, driver } = getMount(key);
if (!driver.setItem) { if (!driver.setItem) {
return // Readonly return; // Readonly
} }
await asyncCall(driver.setItem, relativeKey, stringify(value)) await asyncCall(driver.setItem, relativeKey, stringify(value));
if (!driver.watch) { if (!driver.watch) {
onChange('update', key) onChange("update", key);
} }
}, },
async removeItem (key, removeMeta = true) { async removeItem (key, removeMeta = true) {
key = normalizeKey(key) key = normalizeKey(key);
const { relativeKey, driver } = getMount(key) const { relativeKey, driver } = getMount(key);
if (!driver.removeItem) { if (!driver.removeItem) {
return // Readonly return; // Readonly
} }
await asyncCall(driver.removeItem, relativeKey) await asyncCall(driver.removeItem, relativeKey);
if (removeMeta) { if (removeMeta) {
await asyncCall(driver.removeItem, relativeKey + '$') await asyncCall(driver.removeItem, relativeKey + "$");
} }
if (!driver.watch) { if (!driver.watch) {
onChange('remove', key) onChange("remove", key);
} }
}, },
// Meta // Meta
async getMeta (key, nativeMetaOnly) { async getMeta (key, nativeMetaOnly) {
key = normalizeKey(key) key = normalizeKey(key);
const { relativeKey, driver } = getMount(key) const { relativeKey, driver } = getMount(key);
const meta = Object.create(null) const meta = Object.create(null);
if (driver.getMeta) { if (driver.getMeta) {
Object.assign(meta, await asyncCall(driver.getMeta, relativeKey)) Object.assign(meta, await asyncCall(driver.getMeta, relativeKey));
} }
if (!nativeMetaOnly) { if (!nativeMetaOnly) {
const val = await asyncCall(driver.getItem, relativeKey + '$').then(val => destr(val)) const value = await asyncCall(driver.getItem, relativeKey + "$").then(value_ => destr(value_));
if (val && typeof val === 'object') { if (value && typeof value === "object") {
// TODO: Support date by destr? // TODO: Support date by destr?
if (typeof val.atime === 'string') { val.atime = new Date(val.atime) } if (typeof value.atime === "string") { value.atime = new Date(value.atime); }
if (typeof val.mtime === 'string') { val.mtime = new Date(val.mtime) } if (typeof value.mtime === "string") { value.mtime = new Date(value.mtime); }
Object.assign(meta, val) Object.assign(meta, value);
} }
} }
return meta return meta;
}, },
setMeta (key: string, value: any) { setMeta (key: string, value: any) {
return this.setItem(key + '$', value) return this.setItem(key + "$", value);
}, },
removeMeta (key: string) { removeMeta (key: string) {
return this.removeItem(key + '$') return this.removeItem(key + "$");
}, },
// Keys // Keys
async getKeys (base) { async getKeys (base) {
base = normalizeBaseKey(base) base = normalizeBaseKey(base);
const mounts = getMounts(base, true) const mounts = getMounts(base, true);
let maskedMounts = [] let maskedMounts = [];
const allKeys = [] const allKeys = [];
for (const mount of mounts) { 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 const keys = rawKeys
.map(key => mount.mountpoint + normalizeKey(key)) .map(key => mount.mountpoint + normalizeKey(key))
.filter(key => !maskedMounts.find(p => key.startsWith(p))) .filter(key => !maskedMounts.some(p => key.startsWith(p)));
allKeys.push(...keys) allKeys.push(...keys);
// When /mnt/foo is processed, any key in /mnt with /mnt/foo prefix should be masked // 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 // 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 return base
? allKeys.filter(key => key.startsWith(base!) && !key.endsWith('$')) ? allKeys.filter(key => key.startsWith(base!) && !key.endsWith("$"))
: allKeys.filter(key => !key.endsWith('$')) : allKeys.filter(key => !key.endsWith("$"));
}, },
// Utils // Utils
async clear (base) { async clear (base) {
base = normalizeBaseKey(base) base = normalizeBaseKey(base);
await Promise.all(getMounts(base, false).map(async (m) => { await Promise.all(getMounts(base, false).map(async (m) => {
if (m.driver.clear) { if (m.driver.clear) {
return asyncCall(m.driver.clear) return asyncCall(m.driver.clear);
} }
// Fallback to remove all keys if clear not implemented // Fallback to remove all keys if clear not implemented
if (m.driver.removeItem) { if (m.driver.removeItem) {
const keys = await m.driver.getKeys() const keys = await m.driver.getKeys();
return Promise.all(keys.map(key => m.driver.removeItem!(key))) return Promise.all(keys.map(key => m.driver.removeItem!(key)));
} }
// Readonly // Readonly
})) }));
}, },
async dispose () { 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) { async watch (callback) {
await startWatch() await startWatch();
ctx.watchListeners.push(callback) context.watchListeners.push(callback);
return async () => { return async () => {
ctx.watchListeners = ctx.watchListeners.filter(listener => listener !== callback) context.watchListeners = context.watchListeners.filter(listener => listener !== callback);
if (ctx.watchListeners.length === 0) { if (context.watchListeners.length === 0) {
await stopWatch() await stopWatch();
}
} }
};
}, },
async unwatch () { async unwatch () {
ctx.watchListeners = [] context.watchListeners = [];
await stopWatch() await stopWatch();
}, },
// Mount // Mount
mount (base, driver) { mount (base, driver) {
base = normalizeBaseKey(base) base = normalizeBaseKey(base);
if (base && ctx.mounts[base]) { if (base && context.mounts[base]) {
throw new Error(`already mounted at ${base}`) throw new Error(`already mounted at ${base}`);
} }
if (base) { if (base) {
ctx.mountpoints.push(base) context.mountpoints.push(base);
ctx.mountpoints.sort((a, b) => b.length - a.length) context.mountpoints.sort((a, b) => b.length - a.length);
} }
ctx.mounts[base] = driver context.mounts[base] = driver;
if (ctx.watching) { if (context.watching) {
Promise.resolve(watch(driver, onChange, base)) Promise.resolve(watch(driver, onChange, base))
.then((unwatcher) => { .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) { async unmount (base: string, _dispose = true) {
base = normalizeBaseKey(base) base = normalizeBaseKey(base);
if (!base /* root */ || !ctx.mounts[base]) { if (!base /* root */ || !context.mounts[base]) {
return return;
} }
if (ctx.watching && base in ctx.unwatch) { if (context.watching && base in context.unwatch) {
ctx.unwatch[base]() context.unwatch[base]();
delete ctx.unwatch[base] delete context.unwatch[base];
} }
if (_dispose) { 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 type Snapshot<T=string> = Record<string, T>
export async function snapshot (storage: Storage, base: string): Promise<Snapshot<string>> { export async function snapshot (storage: Storage, base: string): Promise<Snapshot<string>> {
base = normalizeBaseKey(base) base = normalizeBaseKey(base);
const keys = await storage.getKeys(base) const keys = await storage.getKeys(base);
const snapshot: any = {} const snapshot: any = {};
await Promise.all(keys.map(async (key) => { await Promise.all(keys.map(async (key) => {
snapshot[key.substr(base.length)] = await storage.getItem(key) snapshot[key.slice(base.length)] = await storage.getItem(key);
})) }));
return snapshot return snapshot;
} }
export async function restoreSnapshot (driver: Storage, snapshot: Snapshot<StorageValue>, base: string = '') { export async function restoreSnapshot (driver: Storage, snapshot: Snapshot<StorageValue>, base: string = "") {
base = normalizeBaseKey(base) base = normalizeBaseKey(base);
await Promise.all(Object.entries(snapshot).map(e => driver.setItem(base + e[0], e[1]))) await Promise.all(Object.entries(snapshot).map(e => driver.setItem(base + e[0], e[1])));
} }
function watch (driver: Driver, onChange: WatchCallback, base: string) { function watch (driver: Driver, onChange: WatchCallback, base: string) {
if (driver.watch) { return driver.watch ? driver.watch((event, key) => onChange(event, base + key)) : () => {};
return driver.watch((event, key) => onChange(event, base + key))
} else {
return () => undefined
}
} }
async function dispose (driver: Driver) { async function dispose (driver: Driver) {
if (typeof driver.dispose === 'function') { if (typeof driver.dispose === "function") {
await asyncCall(driver.dispose) await asyncCall(driver.dispose);
} }
} }

View File

@ -1,5 +1,5 @@
export type StorageValue = null | string | String | number | Number | boolean | Boolean | object 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 export type WatchCallback = (event: WatchEvent, key: string) => any
type MaybePromise<T> = T | Promise<T> type MaybePromise<T> = T | Promise<T>

View File

@ -1,50 +1,50 @@
import type { Storage } from './types' import type { Storage } from "./types";
type StorageKeys = Array<keyof Storage> type StorageKeys = Array<keyof Storage>
const storageKeyProps: StorageKeys = [ const storageKeyProperties: StorageKeys = [
'hasItem', "hasItem",
'getItem', "getItem",
'setItem', "setItem",
'removeItem', "removeItem",
'getMeta', "getMeta",
'setMeta', "setMeta",
'removeMeta', "removeMeta",
'getKeys', "getKeys",
'clear', "clear",
'mount', "mount",
'unmount' "unmount"
] ];
export function prefixStorage (storage: Storage, base: string) { export function prefixStorage (storage: Storage, base: string) {
base = normalizeBaseKey(base) base = normalizeBaseKey(base);
if (!base) { if (!base) {
return storage return storage;
} }
const nsStorage: Storage = { ...storage } const nsStorage: Storage = { ...storage };
for (const prop of storageKeyProps) { for (const property of storageKeyProperties) {
// @ts-ignore Better types? // @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 storage
.getKeys(base + key, ...args) .getKeys(base + key, ...arguments_)
// Remove Prefix // 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) { export function normalizeKey (key?: string) {
if (!key) { return '' } if (!key) { return ""; }
return key.replace(/[/\\]/g, ':').replace(/:+/g, ':').replace(/^:|:$/g, '') return key.replace(/[/\\]/g, ":").replace(/:+/g, ":").replace(/^:|:$/g, "");
} }
export function joinKeys (...keys: string[]) { export function joinKeys (...keys: string[]) {
return normalizeKey(keys.join(':')) return normalizeKey(keys.join(":"));
} }
export function normalizeBaseKey (base?: string) { export function normalizeBaseKey (base?: string) {
base = normalizeKey(base) base = normalizeKey(base);
return base ? (base + ':') : '' return base ? (base + ":") : "";
} }

View File

@ -1,32 +1,32 @@
import { describe, it, expect } from 'vitest' import { describe, it, expect } from "vitest";
import { listen } from 'listhen' import { listen } from "listhen";
import { $fetch } from 'ohmyfetch' import { $fetch } from "ohmyfetch";
import { createStorage } from '../src' import { createStorage } from "../src";
import { createStorageServer } from '../src/server' import { createStorageServer } from "../src/server";
describe('server', () => { describe("server", () => {
it('basic', async () => { it("basic", async () => {
const storage = createStorage() const storage = createStorage();
const storageServer = createStorageServer(storage) const storageServer = createStorageServer(storage);
const { close, url: serverURL } = await listen(storageServer.handle, { const { close, url: serverURL } = await listen(storageServer.handle, {
port: { random: true } 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.setItem("foo/bar", "bar");
await storage.setMeta('foo/bar', { mtime: new Date() }) await storage.setMeta("foo/bar", { mtime: new Date() });
expect(await fetchStorage('foo/bar')).toBe('bar') expect(await fetchStorage("foo/bar")).toBe("bar");
expect(await fetchStorage('foo/bar', { method: 'PUT', body: 'updated' })).toBe('OK') expect(await fetchStorage("foo/bar", { method: "PUT", body: "updated" })).toBe("OK");
expect(await fetchStorage('foo/bar')).toBe('updated') expect(await fetchStorage("foo/bar")).toBe("updated");
expect(await fetchStorage('/')).toMatchObject(['foo/bar']) expect(await fetchStorage("/")).toMatchObject(["foo/bar"]);
expect(await fetchStorage('foo/bar', { method: 'DELETE' })).toBe('OK') expect(await fetchStorage("foo/bar", { method: "DELETE" })).toBe("OK");
expect(await fetchStorage('foo/bar', {})).toMatchObject([]) expect(await fetchStorage("foo/bar", {})).toMatchObject([]);
await close() await close();
}) });
}) });

View File

@ -1,107 +1,107 @@
import { describe, it, expect, vi } from 'vitest' import { describe, it, expect, vi } from "vitest";
import { createStorage, snapshot, restoreSnapshot, prefixStorage } from '../src' import { createStorage, snapshot, restoreSnapshot, prefixStorage } from "../src";
import memory from '../src/drivers/memory' import memory from "../src/drivers/memory";
const data = { const data = {
'etc:conf': 'test', "etc:conf": "test",
'data:foo': 123 "data:foo": 123
} };
describe('storage', () => { describe("storage", () => {
it('mount/unmount', async () => { it("mount/unmount", async () => {
const storage = createStorage().mount('/mnt', memory()) const storage = createStorage().mount("/mnt", memory());
await restoreSnapshot(storage, data, 'mnt') await restoreSnapshot(storage, data, "mnt");
expect(await snapshot(storage, '/mnt')).toMatchObject(data) expect(await snapshot(storage, "/mnt")).toMatchObject(data);
}) });
it('snapshot', async () => { it("snapshot", async () => {
const storage = createStorage() const storage = createStorage();
await restoreSnapshot(storage, data) await restoreSnapshot(storage, data);
expect(await snapshot(storage, '')).toMatchObject(data) expect(await snapshot(storage, "")).toMatchObject(data);
}) });
it('watch', async () => { it("watch", async () => {
const onChange = vi.fn() const onChange = vi.fn();
const storage = createStorage().mount('/mnt', memory()) const storage = createStorage().mount("/mnt", memory());
await storage.watch(onChange) await storage.watch(onChange);
await restoreSnapshot(storage, data, 'mnt') await restoreSnapshot(storage, data, "mnt");
expect(onChange).toHaveBeenCalledWith('update', 'mnt:etc:conf') expect(onChange).toHaveBeenCalledWith("update", "mnt:etc:conf");
expect(onChange).toHaveBeenCalledWith('update', 'mnt:data:foo') expect(onChange).toHaveBeenCalledWith("update", "mnt:data:foo");
expect(onChange).toHaveBeenCalledTimes(2) expect(onChange).toHaveBeenCalledTimes(2);
}) });
it('unwatch return', async () => { it("unwatch return", async () => {
const onChange = vi.fn() const onChange = vi.fn();
const storage = createStorage().mount('/mnt', memory()) const storage = createStorage().mount("/mnt", memory());
const unwatch = await storage.watch(onChange) const unwatch = await storage.watch(onChange);
await storage.setItem('mnt:data:foo', 42) await storage.setItem("mnt:data:foo", 42);
await unwatch() await unwatch();
await storage.setItem('mnt:data:foo', 41) await storage.setItem("mnt:data:foo", 41);
expect(onChange).toHaveBeenCalledTimes(1) expect(onChange).toHaveBeenCalledTimes(1);
}) });
it('unwatch all', async () => { it("unwatch all", async () => {
const onChange = vi.fn() const onChange = vi.fn();
const storage = createStorage().mount('/mnt', memory()) const storage = createStorage().mount("/mnt", memory());
await storage.watch(onChange) await storage.watch(onChange);
await storage.setItem('mnt:data:foo', 42) await storage.setItem("mnt:data:foo", 42);
await storage.unwatch() await storage.unwatch();
await storage.setItem('mnt:data:foo', 41) await storage.setItem("mnt:data:foo", 41);
expect(onChange).toHaveBeenCalledTimes(1) expect(onChange).toHaveBeenCalledTimes(1);
}) });
it('mount overides', async () => { it("mount overides", async () => {
const mainStorage = memory() const mainStorage = memory();
const storage = createStorage({ driver: mainStorage }) const storage = createStorage({ driver: mainStorage });
await storage.setItem('/mnt/test.txt', 'v1') await storage.setItem("/mnt/test.txt", "v1");
await storage.setItem('/mnt/test.base.txt', 'v1') await storage.setItem("/mnt/test.base.txt", "v1");
const initialKeys = await storage.getKeys() const initialKeys = await storage.getKeys();
expect(initialKeys).toMatchInlineSnapshot(` expect(initialKeys).toMatchInlineSnapshot(`
[ [
"mnt:test.txt", "mnt:test.txt",
"mnt:test.base.txt", "mnt:test.base.txt",
] ]
`) `);
storage.mount('/mnt', memory()) storage.mount("/mnt", memory());
await storage.setItem('/mnt/test.txt', 'v2') await storage.setItem("/mnt/test.txt", "v2");
await storage.setItem('/mnt/foo/test.txt', 'v3') await storage.setItem("/mnt/foo/test.txt", "v3");
storage.mount('/mnt/foo', memory()) storage.mount("/mnt/foo", memory());
expect(await storage.getItem('/mnt/foo/test.txt')).toBe(null) 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(` expect(await storage.getKeys()).toMatchInlineSnapshot(`
[ [
"mnt:test.txt", "mnt:test.txt",
] ]
`) `);
await storage.clear('/mnt') await storage.clear("/mnt");
await storage.unmount('/mnt') await storage.unmount("/mnt");
expect(await storage.getKeys()).toMatchObject(initialKeys) expect(await storage.getKeys()).toMatchObject(initialKeys);
expect(await storage.getItem('/mnt/test.txt')).toBe('v1') expect(await storage.getItem("/mnt/test.txt")).toBe("v1");
}) });
}) });
describe('utils', () => { describe("utils", () => {
it('prefixStorage', async () => { it("prefixStorage", async () => {
const storage = createStorage() const storage = createStorage();
const pStorage = prefixStorage(storage, 'foo') const pStorage = prefixStorage(storage, "foo");
await pStorage.setItem('x', 'bar') await pStorage.setItem("x", "bar");
await pStorage.setItem('y', 'baz') await pStorage.setItem("y", "baz");
expect(await storage.getItem('foo:x')).toBe('bar') expect(await storage.getItem("foo:x")).toBe("bar");
expect(await pStorage.getItem('x')).toBe('bar') expect(await pStorage.getItem("x")).toBe("bar");
expect(await pStorage.getKeys()).toStrictEqual(['x', 'y']) expect(await pStorage.getKeys()).toStrictEqual(["x", "y"]);
// Higher order storage // Higher order storage
const secondStorage = createStorage() const secondStorage = createStorage();
secondStorage.mount('/mnt', storage) secondStorage.mount("/mnt", storage);
const mntStorage = prefixStorage(secondStorage, 'mnt') 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 // 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"]);
}) });
}) });