{"_id":"@artemjs/vfskit","_rev":"3-5ab014ab29c43cf7ae8df9854c4bdefe","name":"@artemjs/vfskit","dist-tags":{"latest":"1.2.1"},"versions":{"1.1.0":{"name":"@artemjs/vfskit","version":"1.1.0","keywords":["vfs","filesystem","fs","s3","storage","abstraction","virtual-file-system","encryption","remote","adapter","node"],"license":"MIT","_id":"@artemjs/vfskit@1.1.0","maintainers":[{"name":"artemjs","email":"artemjson@gmail.com"}],"homepage":"https://github.com/artemjs/vfskit#readme","bugs":{"url":"https://github.com/artemjs/vfskit/issues"},"dist":{"shasum":"c7ad683e50b4974ea7a1a4225838c8cc54d1eb0a","tarball":"https://registry.npmjs.org/@artemjs/vfskit/-/vfskit-1.1.0.tgz","fileCount":7,"integrity":"sha512-2VYBUutDS6VY06kPHSRewQ+8kMwFvdEgGoS6776A/emhqVkk+g7J0ALyq39j591gYOArxHiz9PBQFZiEHw3D8Q==","signatures":[{"sig":"MEYCIQC5ta+wtf0CxuD4VCIu/fzpmMSdZr/uBCAAcjdmsZEGPgIhAKYficlpBs2H0GC0Pz0zefovUhTlUnJMKSVU9kQcsBsc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":42532},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./conformance":{"types":"./dist/conformance.d.ts","import":"./dist/conformance.js"}},"gitHead":"5f072b66ef7a53acfd79eda99ce0eb34005e177a","scripts":{"build":"tsup"},"_npmUser":{"name":"artemjs","email":"artemjson@gmail.com"},"repository":{"url":"git+https://github.com/artemjs/vfskit.git","type":"git","directory":"facades/vfskit"},"_npmVersion":"11.12.1","description":"Universal abstraction over any virtual file system - memory, disk, S3 and more - with composable adapters, encryption and a remote bridge. Backend kit.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","@vfskit/s3":"*","@vfskit/core":"*","@vfskit/cache":"*","@vfskit/memory":"*","@vfskit/remote":"*","@vfskit/server":"*","@vfskit/encrypt":"*","@vfskit/node-fs":"*","@vfskit/transport-ws":"*","@vfskit/transport-http":"*"},"_npmOperationalInternal":{"tmp":"tmp/vfskit_1.1.0_1782676739179_0.4520115366667765","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@artemjs/vfskit","version":"1.2.0","keywords":["vfs","filesystem","fs","s3","storage","abstraction","virtual-file-system","encryption","remote","adapter","node"],"license":"MIT","_id":"@artemjs/vfskit@1.2.0","maintainers":[{"name":"artemjs","email":"artemjson@gmail.com"}],"homepage":"https://github.com/artemjs/vfskit#readme","bugs":{"url":"https://github.com/artemjs/vfskit/issues"},"dist":{"shasum":"3437d3073204f8a22009c55112b220269ca7352d","tarball":"https://registry.npmjs.org/@artemjs/vfskit/-/vfskit-1.2.0.tgz","fileCount":7,"integrity":"sha512-rZRq8d2xaQ4nA1MazD4dNcImxdHjctsitwX8Y5KxmMJJJqzeGLFMZHSlws5UDmeJ96HggwN3khxiBY8zmNvKKQ==","signatures":[{"sig":"MEUCIQD7Lha5v5IJt2YEY7gwOJgC0vnL87SwuILPzX4E1NCIuwIgbeKoAZTbAtDgKTsTJ7EKhnkGuNPD5PZCFfREvmjAZoQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49959},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./conformance":{"types":"./dist/conformance.d.ts","import":"./dist/conformance.js"}},"gitHead":"2413c2179a9486f5f5cf6890853823acd04e9c5c","scripts":{"build":"tsup"},"_npmUser":{"name":"artemjs","email":"artemjson@gmail.com"},"repository":{"url":"git+https://github.com/artemjs/vfskit.git","type":"git","directory":"facades/vfskit"},"_npmVersion":"11.12.1","description":"Universal abstraction over any virtual file system - memory, disk, S3 and more - with composable adapters, encryption and a remote bridge. Backend kit.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","@vfskit/kv":"*","@vfskit/s3":"*","@vfskit/core":"*","@vfskit/cache":"*","@vfskit/memory":"*","@vfskit/remote":"*","@vfskit/server":"*","@vfskit/sqlite":"*","@vfskit/encrypt":"*","@vfskit/node-fs":"*","@vfskit/transport-ws":"*","@vfskit/transport-http":"*"},"_npmOperationalInternal":{"tmp":"tmp/vfskit_1.2.0_1782679978964_0.13540691079266054","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@artemjs/vfskit","version":"1.2.1","description":"Universal abstraction over any virtual file system - memory, disk, S3 and more - with composable adapters, encryption and a remote bridge. Backend kit.","keywords":["vfs","filesystem","fs","s3","storage","abstraction","virtual-file-system","encryption","remote","adapter","node"],"license":"MIT","type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./conformance":{"types":"./dist/conformance.d.ts","import":"./dist/conformance.js"}},"main":"./dist/index.js","types":"./dist/index.d.ts","sideEffects":false,"repository":{"type":"git","url":"git+https://github.com/artemjs/vfskit.git","directory":"facades/vfskit"},"bugs":{"url":"https://github.com/artemjs/vfskit/issues"},"homepage":"https://github.com/artemjs/vfskit#readme","scripts":{"build":"tsup"},"devDependencies":{"@vfskit/core":"*","@vfskit/memory":"*","@vfskit/node-fs":"*","@vfskit/s3":"*","@vfskit/encrypt":"*","@vfskit/server":"*","@vfskit/remote":"*","@vfskit/transport-http":"*","@vfskit/transport-ws":"*","tsup":"^8.3.0","@vfskit/cache":"*","@vfskit/kv":"*","@vfskit/sqlite":"*"},"publishConfig":{"access":"public"},"gitHead":"3cd9b0aa0f321164d3df5db3e32f9588ad3a682c","_id":"@artemjs/vfskit@1.2.1","_nodeVersion":"26.0.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-MG0Pvpvls1VT+pFzT3JQUJ0O3PEh1fLk+I52p/+8D4wRjmgqcP1T2fta3ecgV6LYD1nWKAL3cw/VnhJOclhsGA==","shasum":"1b32cbab73d79a92e01d8a77b2b1b79fe785845f","tarball":"https://registry.npmjs.org/@artemjs/vfskit/-/vfskit-1.2.1.tgz","fileCount":7,"unpackedSize":52598,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCyz7ILywbPFw27DGDBJkpD3hw0cA1/zSZPRb72zpteOwIgfCOu2xygB3Vth8QALuAQaLlA0GcOoh57USDbactD2E4="}]},"_npmUser":{"name":"artemjs","email":"artemjson@gmail.com"},"directories":{},"maintainers":[{"name":"artemjs","email":"artemjson@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vfskit_1.2.1_1782681679806_0.3073982180414039"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-28T19:58:58.882Z","modified":"2026-06-28T21:21:20.045Z","1.1.0":"2026-06-28T19:58:59.298Z","1.2.0":"2026-06-28T20:52:59.103Z","1.2.1":"2026-06-28T21:21:19.946Z"},"bugs":{"url":"https://github.com/artemjs/vfskit/issues"},"license":"MIT","homepage":"https://github.com/artemjs/vfskit#readme","keywords":["vfs","filesystem","fs","s3","storage","abstraction","virtual-file-system","encryption","remote","adapter","node"],"repository":{"type":"git","url":"git+https://github.com/artemjs/vfskit.git","directory":"facades/vfskit"},"description":"Universal abstraction over any virtual file system - memory, disk, S3 and more - with composable adapters, encryption and a remote bridge. Backend kit.","maintainers":[{"name":"artemjs","email":"artemjson@gmail.com"}],"readme":"<p align=\"center\">\n  <img src=\"https://cdn.jsdelivr.net/gh/artemjs/vfskit@6dffa9f/assets/logo.svg\" alt=\"vfskit\" width=\"160\" height=\"160\">\n</p>\n\n<h1 align=\"center\">vfskit</h1>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/version-1.2.1-7c8cff?style=flat-square\" alt=\"version\">\n  <img src=\"https://img.shields.io/badge/license-MIT-56e6c4?style=flat-square\" alt=\"license\">\n  <img src=\"https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square\" alt=\"typescript\">\n  <img src=\"https://img.shields.io/badge/module-ESM-f0db4f?style=flat-square\" alt=\"esm\">\n  <img src=\"https://img.shields.io/badge/runtime%20deps-0-1f9d55?style=flat-square\" alt=\"zero deps\">\n</p>\n\n<p align=\"center\">\n  <b>One <code>VFS</code> interface over any backend</b>: in-memory, real disk, S3, or your own.<br>\n  Composable adapters, encryption, caching, optimistic concurrency, and a browser&nbsp;&#8646;&nbsp;server bridge.\n</p>\n\n---\n\nvfskit wraps any kind of storage behind a single, small `VFS` interface, then lets you\n**compose** behavior on top of it - encryption, caching, a remote bridge - and drive it from\nthe browser exactly as you would on the server. Anything you can read, write, and list\nbecomes a structured file system with files and metadata.\n\nIt ships in two faces under one brand:\n\n- **`@artemjs/vfskit`** (npm, Node) - the full kit: core + memory + node-fs + s3 + sqlite + kv + encrypt + cache + serve + remote.\n- **`@artemjs/vfskit-front`** (npm + jsDelivr, browser) - core + memory + opfs + kv + encrypt + cache + a remote client.\n\nBoth expose **identical API names**, so your code looks the same on either side.\n\n## Install\n\n```sh\nnpm i @artemjs/vfskit              # backend / Node\n```\n\n```js\n// browser, no build step\nimport { remote, wsTransport } from 'https://cdn.jsdelivr.net/npm/@artemjs/vfskit-front/+esm'\n```\n\n## Everything is a VFS\n\n<p align=\"center\">\n  <img src=\"https://cdn.jsdelivr.net/gh/artemjs/vfskit@6dffa9f/assets/architecture.svg\" alt=\"vfskit architecture\" width=\"820\">\n</p>\n\n```ts\ninterface VFS {\n  read(path): Promise<Uint8Array>\n  write(path, data, opts?): Promise<void>\n  list(path, opts?): Promise<Entry[]>\n  stat(path): Promise<Stat>\n  exists(path): Promise<boolean>\n  mkdir(path, opts?): Promise<void>\n  remove(path, opts?): Promise<void>\n  move(from, to): Promise<void>\n  copy(from, to): Promise<void>\n  getMeta(path): Promise<Meta>\n  setMeta(path, meta): Promise<void>\n  watch(path, cb): Unsubscribe\n  capabilities(): Capabilities\n}\n```\n\n- **Adapters** implement `VFS` over a backend: `memory()`, `nodeFs(dir)`, `s3({ client })`.\n- **Middleware** wraps a `VFS` and returns a `VFS`: `encrypt(vfs, { passphrase })`, `cache(vfs)`.\n- **Bridge** connects them across the wire: `serve(vfs)` on the server, `remote(transport)` on the client.\n\nCompose freely. `encrypt(remote(transport))` is end-to-end encryption - the server only ever\nstores ciphertext.\n\n## Quick start\n\n```ts\nimport { memory, nodeFs, encrypt, serve, remote, toText } from '@artemjs/vfskit'\n\nconst store = encrypt(nodeFs('./data'), { passphrase: 'hunter2' })\nawait store.write('/notes/todo.md', '# buy milk', { meta: { tag: 'home' } })\nconsole.log(toText(await store.read('/notes/todo.md')))\n```\n\nExpose a backend, drive it from anywhere:\n\n```ts\n// server\nconst server = serve(nodeFs('./data'))\n// wire server.fetch (HTTP) or server.socket (WebSocket) into your runtime\n```\n\n```ts\n// client (browser or Node)\nimport { remote, wsTransport } from '@artemjs/vfskit-front'\nconst fs = remote(wsTransport('ws://localhost:3000'))\nawait fs.write('/hello.txt', 'hi')\n```\n\n## Adapters\n\n| Adapter | Where | Metadata | Notes |\n| --- | --- | --- | --- |\n| `memory()` | anywhere | native | reference implementation; synchronous `watch` |\n| `nodeFs(dir)` | Node | sidecar `.vfskit/meta.json` | rooted at `dir`; native streaming; `watch` via `fs.watch` |\n| `s3({ client, prefix?, pollMs? })` | Node | native object metadata | inject any `S3Like` client; POSIX dirs emulated with markers; `watch` by polling |\n| `sqlite(file)` | Node | native column | whole VFS in one SQLite file via built-in `node:sqlite`; conditional writes |\n| `opfs(root?)` | browser | sidecar manifest | persistent Origin Private File System; native file streaming |\n| `kv({ store, prefix? })` | anywhere | native record | over any async key-value store; ships `memKv()` and `localStorageKv()` |\n\nEvery adapter passes the same conformance suite, so a new one \"just works\" once it does too.\n\n```ts\n// browser-persistent storage, no server\nimport { opfs, kv, localStorageKv } from '@artemjs/vfskit-front'\nconst disk = opfs()                          // Origin Private File System\nconst ls = kv({ store: localStorageKv() })   // or back any KV: Redis, Cloudflare KV, Deno KV\n\n// node: a whole file system in one .db\nimport { sqlite } from '@artemjs/vfskit'\nconst db = sqlite('./app.db')\n```\n\n## Bring your own storage\n\nvfskit is just an interface. To put *any* backend behind the same API, write a function that\nreturns a `VFS` - a plain object literal implementing the methods above - over your store\n(a database, a KV cache, `localStorage`, a blob service, whatever):\n\n```ts\nimport { type VFS, normalize, toBytes, notFound } from '@artemjs/vfskit'\n\nexport function myVfs(store: MyStore): VFS {\n  return {\n    capabilities: () => ({ streaming: false, watch: false, atomicMove: false, nativeMeta: true, randomAccess: false, conditionalWrite: false }),\n    async read(path) { /* ... */ },\n    async write(path, data, opts) { /* ... */ },\n    // ...the rest of the interface\n  }\n}\n```\n\nThen validate it against the exact same battery every built-in adapter must pass:\n\n```ts\nimport { conformanceCases } from '@artemjs/vfskit/conformance'\nimport { describe, it } from 'vitest'\n\ndescribe('my adapter', () => {\n  for (const c of conformanceCases) it(c.name, () => c.run(() => myVfs(new MyStore())))\n})\n```\n\nIf it passes, your storage now works everywhere vfskit works - behind `encrypt(...)`, behind\n`serve(...)`, driven by a browser `remote(...)`. A complete worked example (a key-value\nbackend) lives in [`examples/custom-adapter`](examples/custom-adapter). `conformanceCases` is\nframework-agnostic (`{ name, run(makeVfs) }[]`), so you can drive it from any test runner.\n\n## Encryption\n\nAES-256-GCM via WebCrypto. A raw key, or a passphrase derived per file with PBKDF2 (random\nsalt, 210k iterations). Tamper fails closed with a typed error. Content is encrypted by\ndefault; metadata stays as the backend stores it.\n\n```ts\nconst vault = encrypt(memory(), { passphrase: 'open sesame' })\n```\n\n## Caching\n\n`cache(vfs, { ttlMs? })` serves reads from an in-memory store (write-through,\nsubtree-invalidated on write/remove/move/copy). Wrap a `remote(...)` to avoid round-trips for\nhot files:\n\n```ts\nimport { cache, remote, wsTransport } from '@artemjs/vfskit-front'\nconst fs = cache(remote(wsTransport(url)), { ttlMs: 5000 })\n```\n\nPass your own `store` to back the cache with anything (e.g. `localStorage`).\n\n## Concurrent writes\n\nAdapters that report `conditionalWrite` give every file an opaque `version` token (via\n`stat`). Pass it back as `ifMatch` to make a write succeed only if nobody changed the file in\nbetween - otherwise it fails with a typed `CONFLICT`. `ifAbsent` makes a create-only write.\n\n```ts\nconst { version } = await fs.stat('/doc')\nawait fs.write('/doc', next, { ifMatch: version })   // CONFLICT if it moved on\nawait fs.write('/new', data, { ifAbsent: true })     // ALREADY_EXISTS if it exists\n```\n\nSupported by `memory`, `nodeFs`, `s3`, and transparently over `remote(...)`.\n\n## Streaming\n\n`readStream(vfs, path)` and `writeStream(vfs, path)` give Web `ReadableStream` /\n`WritableStream` over any adapter - native where supported (`nodeFs` streams real file\nhandles), buffered otherwise, so the API is uniform:\n\n```ts\nimport { readStream, writeStream, collect, toBytes } from '@artemjs/vfskit'\n\nconst w = (await writeStream(fs, '/big.log')).getWriter()\nawait w.write(toBytes('line 1\\n')); await w.close()\nconst all = await collect(await readStream(fs, '/big.log'))\n```\n\n`encrypt` and `cache` buffer through their own `read`/`write`, so streaming stays correct\nbehind them (the stream still yields plaintext; the disk still holds ciphertext).\n\n## Transports\n\n- `httpTransport(url)` - request/response; works on serverless/edge. No `watch`.\n- `wsTransport(url)` - multiplexed; enables `watch`/events.\n\n## Errors\n\nTyped hierarchy with stable wire codes, reconstructed on the client across the bridge:\n`NOT_FOUND`, `ALREADY_EXISTS`, `NOT_A_DIRECTORY`, `IS_A_DIRECTORY`, `PERMISSION_DENIED`,\n`UNSUPPORTED`, `CONFLICT`, `IO`. Detect with `isVfsError(e)` (brand-based - survives bundling\nand the RPC round-trip).\n\n## Example\n\n[`examples/cloud-ide`](examples/cloud-ide) - Monaco editing files on a real-disk VFS over a\nWebSocket bridge, with per-user isolation. Swapping the backend to S3 is one line.\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md"}