diff --git a/.eslintrc b/.eslintrc index e14cbd6..7efeea3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,9 @@ { "extends": [ - "@nuxtjs/eslint-config-typescript" - ] + "eslint-config-unjs" + ], + "rules": { + "unicorn/no-null": 0, + "unicorn/prevent-abbreviations": 0 + } } diff --git a/LICENSE b/LICENSE index 9e0c26f..e739abc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 - UnJS +Copyright (c) Pooya Parsa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 20e98a4..4db8e1d 100644 --- a/README.md +++ b/README.md @@ -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') diff --git a/build.config.ts b/build.config.ts index 468fcbb..c5a0c9d 100644 --- a/build.config.ts +++ b/build.config.ts @@ -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 } ] -}) +}); diff --git a/demo/vite.config.ts b/demo/vite.config.ts index 69c0c21..73634a5 100644 --- a/demo/vite.config.ts +++ b/demo/vite.config.ts @@ -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); } } ] -}) +}); diff --git a/package.json b/package.json index 3112627..5e5d6f5 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b978045..fc04bfb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: diff --git a/renovate.json b/renovate.json index 217d821..a9971c8 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,5 @@ { "extends": [ - "@nuxtjs" + "github>unjs/renovate-config" ] } diff --git a/src/_utils.ts b/src/_utils.ts index e522059..db08b64 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -1,26 +1,26 @@ type Awaited = T extends Promise ? Awaited : T; type Promisified = Promise> -export function wrapToPromise (val: T) { - if (!val || typeof (val as any).then !== 'function') { - return Promise.resolve(val) as Promisified +export function wrapToPromise (value: T) { + if (!value || typeof (value as any).then !== "function") { + return Promise.resolve(value) as Promisified; } - return val as unknown as Promisified + return value as unknown as Promisified; } -export function asyncCall any>(fn: T, ...args: any[]): Promisified> { +export function asyncCall any>(function_: T, ...arguments_: any[]): Promisified> { 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); } diff --git a/src/cli.ts b/src/cli.ts index 0e7e917..d879345 100644 --- a/src/cli.ts +++ b/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); +}); diff --git a/src/index.ts b/src/index.ts index 662b30c..769c200 100644 --- a/src/index.ts +++ b/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 diff --git a/src/server.ts b/src/server.ts index 724e72c..c5743dc 100644 --- a/src/server.ts +++ b/src/server.ts @@ -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) - } + }; } diff --git a/src/storage.ts b/src/storage.ts index bc32158..552ca36 100644 --- a/src/storage.ts +++ b/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 @@ -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 = Record export async function snapshot (storage: Storage, base: string): Promise> { - 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, 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, 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); } } diff --git a/src/types.ts b/src/types.ts index 4a4ca4e..2c7b820 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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 | Promise diff --git a/src/utils.ts b/src/utils.ts index 532c484..38b41a0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,50 +1,50 @@ -import type { Storage } from './types' +import type { Storage } from "./types"; type StorageKeys = Array -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 + ":") : ""; } diff --git a/test/server.test.ts b/test/server.test.ts index 6c681ba..8b52254 100644 --- a/test/server.test.ts +++ b/test/server.test.ts @@ -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(); + }); +}); diff --git a/test/storage.test.ts b/test/storage.test.ts index c3d52ab..fcc3483 100644 --- a/test/storage.test.ts +++ b/test/storage.test.ts @@ -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"]); + }); +});