@artemjs/vfskit 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -3
- package/dist/index.d.ts +17 -1
- package/dist/index.js +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<h1 align="center">vfskit</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img src="https://img.shields.io/badge/version-1.
|
|
8
|
+
<img src="https://img.shields.io/badge/version-1.2.0-7c8cff?style=flat-square" alt="version">
|
|
9
9
|
<img src="https://img.shields.io/badge/license-MIT-56e6c4?style=flat-square" alt="license">
|
|
10
10
|
<img src="https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square" alt="typescript">
|
|
11
11
|
<img src="https://img.shields.io/badge/module-ESM-f0db4f?style=flat-square" alt="esm">
|
|
@@ -26,8 +26,8 @@ becomes a structured file system with files and metadata.
|
|
|
26
26
|
|
|
27
27
|
It ships in two faces under one brand:
|
|
28
28
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
29
|
+
- **`@artemjs/vfskit`** (npm, Node) - the full kit: core + memory + node-fs + s3 + sqlite + kv + encrypt + cache + serve + remote.
|
|
30
|
+
- **`@artemjs/vfskit-front`** (npm + jsDelivr, browser) - core + memory + opfs + kv + encrypt + cache + a remote client.
|
|
31
31
|
|
|
32
32
|
Both expose **identical API names**, so your code looks the same on either side.
|
|
33
33
|
|
|
@@ -105,9 +105,23 @@ await fs.write('/hello.txt', 'hi')
|
|
|
105
105
|
| `memory()` | anywhere | native | reference implementation; synchronous `watch` |
|
|
106
106
|
| `nodeFs(dir)` | Node | sidecar `.vfskit/meta.json` | rooted at `dir`; native streaming; `watch` via `fs.watch` |
|
|
107
107
|
| `s3({ client, prefix?, pollMs? })` | Node | native object metadata | inject any `S3Like` client; POSIX dirs emulated with markers; `watch` by polling |
|
|
108
|
+
| `sqlite(file)` | Node | native column | whole VFS in one SQLite file via built-in `node:sqlite`; conditional writes |
|
|
109
|
+
| `opfs(root?)` | browser | sidecar manifest | persistent Origin Private File System; native file streaming |
|
|
110
|
+
| `kv({ store, prefix? })` | anywhere | native record | over any async key-value store; ships `memKv()` and `localStorageKv()` |
|
|
108
111
|
|
|
109
112
|
Every adapter passes the same conformance suite, so a new one "just works" once it does too.
|
|
110
113
|
|
|
114
|
+
```ts
|
|
115
|
+
// browser-persistent storage, no server
|
|
116
|
+
import { opfs, kv, localStorageKv } from '@artemjs/vfskit-front'
|
|
117
|
+
const disk = opfs() // Origin Private File System
|
|
118
|
+
const ls = kv({ store: localStorageKv() }) // or back any KV: Redis, Cloudflare KV, Deno KV
|
|
119
|
+
|
|
120
|
+
// node: a whole file system in one .db
|
|
121
|
+
import { sqlite } from '@artemjs/vfskit'
|
|
122
|
+
const db = sqlite('./app.db')
|
|
123
|
+
```
|
|
124
|
+
|
|
111
125
|
## Bring your own storage
|
|
112
126
|
|
|
113
127
|
vfskit is just an interface. To put *any* backend behind the same API, write a function that
|
package/dist/index.d.ts
CHANGED
|
@@ -68,6 +68,22 @@ interface S3Opts {
|
|
|
68
68
|
declare function memoryS3(): S3Like;
|
|
69
69
|
declare function s3(opts: S3Opts): VFS;
|
|
70
70
|
|
|
71
|
+
declare function sqlite(filename: string): VFS;
|
|
72
|
+
|
|
73
|
+
interface KvStore {
|
|
74
|
+
get(key: string): Promise<string | null | undefined>;
|
|
75
|
+
set(key: string, value: string): Promise<void>;
|
|
76
|
+
delete(key: string): Promise<void>;
|
|
77
|
+
list(prefix: string): Promise<string[]>;
|
|
78
|
+
}
|
|
79
|
+
interface KvOpts {
|
|
80
|
+
store: KvStore;
|
|
81
|
+
prefix?: string;
|
|
82
|
+
}
|
|
83
|
+
declare function memKv(): KvStore;
|
|
84
|
+
declare function localStorageKv(ls?: Storage): KvStore;
|
|
85
|
+
declare function kv(opts: KvOpts): VFS;
|
|
86
|
+
|
|
71
87
|
interface EncryptOpts {
|
|
72
88
|
key?: Uint8Array;
|
|
73
89
|
passphrase?: string;
|
|
@@ -133,4 +149,4 @@ interface SocketLike {
|
|
|
133
149
|
}
|
|
134
150
|
declare function wsTransport(url: string, factory?: () => SocketLike): Transport;
|
|
135
151
|
|
|
136
|
-
export { BRAND, BytesLike, type CacheOpts, type CacheStore, Capabilities, type EncryptOpts, type ErrorCode, type FetchLike, Meta, ReadOpts, type RemoteOpts, type S3Like, type S3Object, type S3Opts, type Server, type Socket, type SocketLike, type Transport, Unsubscribe, VFS, VfsError, WatchCb, WriteOpts, alreadyExists, basename, cache, collect, concat, conflict, dirname, encrypt, httpTransport, io, isADirectory, isVfsError, join, memory, memoryS3, nodeFs, normalize, notADirectory, notFound, permissionDenied, readStream, remote, s3, segments, serve, toBytes, toText, unsupported, writeStream, wsTransport };
|
|
152
|
+
export { BRAND, BytesLike, type CacheOpts, type CacheStore, Capabilities, type EncryptOpts, type ErrorCode, type FetchLike, type KvOpts, type KvStore, Meta, ReadOpts, type RemoteOpts, type S3Like, type S3Object, type S3Opts, type Server, type Socket, type SocketLike, type Transport, Unsubscribe, VFS, VfsError, WatchCb, WriteOpts, alreadyExists, basename, cache, collect, concat, conflict, dirname, encrypt, httpTransport, io, isADirectory, isVfsError, join, kv, localStorageKv, memKv, memory, memoryS3, nodeFs, normalize, notADirectory, notFound, permissionDenied, readStream, remote, s3, segments, serve, sqlite, toBytes, toText, unsupported, writeStream, wsTransport };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function m(n){let r=[];for(let o of n.split("/"))if(!(o===""||o===".")){if(o===".."){r.pop();continue}r.push(o)}return"/"+r.join("/")}function Et(...n){return m(n.join("/"))}function V(n){let r=m(n),o=r.lastIndexOf("/");return o<=0?"/":r.slice(0,o)}function Mt(n){let r=m(n);return r.slice(r.lastIndexOf("/")+1)}function Dt(n){return m(n).split("/").filter(Boolean)}var Y=Symbol.for("vfskit.VfsError"),b=class extends Error{code;path;[Y]=!0;constructor(r,o,i){super(o),this.name="VfsError",this.code=r,this.path=i}},v=n=>new b("NOT_FOUND",`not found: ${n}`,n),S=n=>new b("ALREADY_EXISTS",`already exists: ${n}`,n),M=n=>new b("NOT_A_DIRECTORY",`not a directory: ${n}`,n),D=n=>new b("IS_A_DIRECTORY",`is a directory: ${n}`,n),Rt=n=>new b("PERMISSION_DENIED",`permission denied: ${n}`,n),z=n=>new b("UNSUPPORTED",`unsupported: ${n}`),F=n=>new b("CONFLICT",`version conflict: ${n}`,n),x=(n,r)=>new b("IO",n,r);function Pt(n){return typeof n=="object"&&n!==null&&n[Y]===!0}var et=new TextEncoder,rt=new TextDecoder;function O(n){return typeof n=="string"?et.encode(n):n instanceof Uint8Array?n:new Uint8Array(n)}function Ct(n){return rt.decode(n)}function L(n){let r=0;for(let c of n)r+=c.length;let o=new Uint8Array(r),i=0;for(let c of n)o.set(c,i),i+=c.length;return o}async function Vt(n,r,o){if(n.readStream)return n.readStream(r,o);let i=await n.read(r,o);return new ReadableStream({start(c){c.enqueue(i),c.close()}})}async function Lt(n,r,o){if(n.writeStream)return n.writeStream(r,o);let i=[];return new WritableStream({write(c){i.push(c)},async close(){await n.write(r,L(i),o)}})}async function It(n){let r=[],o=n.getReader();for(;;){let{done:i,value:c}=await o.read();if(i)break;c&&r.push(c)}return L(r)}var nt={streaming:!1,watch:!0,atomicMove:!0,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0};function at(){let n=()=>Date.now(),r=0,o=()=>String(++r),i=new Map([["/",{type:"dir",meta:{},mtime:n(),ctime:n()}]]),c=new Set,u=(e,a)=>a===e||a.startsWith(e==="/"?"/":e+"/"),l=(e,a)=>{for(let t of c)u(t.base,a)&&t.cb({type:e,path:a})},d=e=>{let a=i.get(e);if(!a)throw v(e);return a},f=e=>{let a=V(e),t=i.get(a);if(!t)throw v(a);if(t.type!=="dir")throw M(a)};return{capabilities:()=>nt,async read(e){let a=m(e),t=d(a);if(t.type==="dir")throw D(a);return t.data.slice()},async write(e,a,t){let s=m(e);f(s);let y=i.get(s);if(y&&y.type==="dir")throw D(s);if(t?.ifAbsent&&y)throw S(s);if(t?.ifMatch!==void 0&&y?.version!==t.ifMatch)throw F(s);let p=y?y.ctime:n();i.set(s,{type:"file",data:O(a).slice(),meta:t?.meta?{...t.meta}:y?.meta??{},ctime:p,mtime:n(),version:o()}),l(y?"update":"create",s)},async list(e,a){let t=m(e);if(d(t).type!=="dir")throw M(t);let y=[];for(let[p,w]of i)p===t||!u(t,p)||!a?.recursive&&V(p)!==t||y.push({name:p.slice(p.lastIndexOf("/")+1),path:p,type:w.type});return y},async stat(e){let a=m(e),t=d(a);return{type:t.type,size:t.type==="file"?t.data.length:0,mtime:t.mtime,ctime:t.ctime,meta:{...t.meta},version:t.type==="file"?t.version:void 0}},async exists(e){return i.has(m(e))},async mkdir(e,a){let t=m(e);if(i.has(t)){if(a?.recursive)return;throw S(t)}if(a?.recursive){let s="";for(let y of t.split("/").filter(Boolean)){s+="/"+y;let p=i.get(s);if(p){if(p.type!=="dir")throw M(s);continue}i.set(s,{type:"dir",meta:{},mtime:n(),ctime:n()}),l("create",s)}return}f(t),i.set(t,{type:"dir",meta:{},mtime:n(),ctime:n()}),l("create",t)},async remove(e,a){let t=m(e);d(t);let s=[...i.keys()].filter(y=>y!==t&&u(t,y));if(s.length&&!a?.recursive)throw x("directory not empty",t);for(let y of[...s,t])i.delete(y),l("remove",y)},async move(e,a){let t=m(e),s=m(a);if(d(t),f(s),i.has(s))throw S(s);if(u(t,s))throw x("cannot move into itself",s);for(let y of[...i.keys()].filter(p=>p===t||u(t,p))){let p=i.get(y);i.delete(y),i.set(s+y.slice(t.length),p)}l("remove",t),l("create",s)},async copy(e,a){let t=m(e),s=m(a);if(d(t),f(s),i.has(s))throw S(s);for(let y of[...i.keys()].filter(p=>p===t||u(t,p))){let p=i.get(y);i.set(s+y.slice(t.length),p.type==="file"?{type:"file",data:p.data.slice(),meta:{...p.meta},mtime:p.mtime,ctime:p.ctime,version:o()}:{type:"dir",meta:{...p.meta},mtime:p.mtime,ctime:p.ctime})}l("create",s)},async getMeta(e){return{...d(m(e)).meta}},async setMeta(e,a){let t=d(m(e));t.meta={...a},t.mtime=n()},watch(e,a){let t={base:m(e),cb:a};return c.add(t),()=>{c.delete(t)}}}}import{promises as g,watch as $,createReadStream as it}from"fs";import{Readable as st}from"stream";import{join as ot,dirname as C}from"path";var ct={streaming:!0,watch:!0,atomicMove:!0,nativeMeta:!1,randomAccess:!1,conditionalWrite:!0},J="/.vfskit/meta.json",K="/.vfskit/ver.json";function yt(n){let r=e=>ot(n,m(e)),o=e=>e==="/.vfskit"||e.startsWith("/.vfskit/"),i=async()=>{try{return JSON.parse(new TextDecoder().decode(await g.readFile(r(J))))}catch{return{}}},c=async e=>{await g.mkdir(C(r(J)),{recursive:!0}),await g.writeFile(r(J),new TextEncoder().encode(JSON.stringify(e)))},u=async()=>{try{return JSON.parse(new TextDecoder().decode(await g.readFile(r(K))))}catch{return{}}},l=async e=>{await g.mkdir(C(r(K)),{recursive:!0}),await g.writeFile(r(K),new TextEncoder().encode(JSON.stringify(e)))},d=(e,a,t,s)=>{let y=!1;for(let p of Object.keys(e))(p===a||p.startsWith(a+"/"))&&(e[t+p.slice(a.length)]=e[p],s||delete e[p],y=!0);return y},f=async(e,a)=>{try{return await a()}catch(t){if(t instanceof b)throw t;let s=t.code;throw s==="ENOENT"?v(e):s==="EEXIST"?S(e):s==="ENOTDIR"?M(e):s==="EISDIR"?D(e):x(String(t.message??t),e)}};return{capabilities:()=>ct,async read(e){let a=m(e);return f(a,async()=>{if((await g.stat(r(a))).isDirectory())throw D(a);return new Uint8Array(await g.readFile(r(a)))})},async write(e,a,t){let s=m(e);await f(s,()=>g.access(C(r(s))).then(()=>{}));let y=await u();if(t?.ifAbsent||t?.ifMatch!==void 0){let p=await g.stat(r(s)).then(()=>!0,()=>!1);if(t.ifAbsent&&p)throw S(s);if(t.ifMatch!==void 0&&String(y[s]??"")!==t.ifMatch)throw F(s)}if(await f(s,()=>g.writeFile(r(s),O(a)).then(()=>{})),y[s]=(y[s]??0)+1,await l(y),t?.meta){let p=await i();p[s]=t.meta,await c(p)}},async list(e,a){let t=m(e),s=[],y=async p=>{for(let w of await g.readdir(r(p),{withFileTypes:!0})){let h=m(p+"/"+w.name);o(h)||(s.push({name:w.name,path:h,type:w.isDirectory()?"dir":"file"}),a?.recursive&&w.isDirectory()&&await y(h))}};return await f(t,async()=>{if(!(await g.stat(r(t))).isDirectory())throw M(t);await y(t)}),s},async stat(e){let a=m(e);return f(a,async()=>{let t=await g.stat(r(a)),s=t.isDirectory(),y=s?void 0:(await u())[a];return{type:s?"dir":"file",size:s?0:t.size,mtime:t.mtimeMs,ctime:t.ctimeMs,meta:(await i())[a]??{},version:y!=null?String(y):void 0}})},async exists(e){try{return await g.stat(r(m(e))),!0}catch{return!1}},async mkdir(e,a){let t=m(e);await f(t,()=>g.mkdir(r(t),{recursive:a?.recursive}).then(()=>{}))},async remove(e,a){let t=m(e);await f(t,async()=>{if((await g.stat(r(t))).isDirectory()){if((await g.readdir(r(t))).length&&!a?.recursive)throw x("directory not empty",t);await g.rm(r(t),{recursive:!0,force:!0})}else await g.rm(r(t))});let s=await i(),y=!1;for(let h of Object.keys(s))(h===t||h.startsWith(t+"/"))&&(delete s[h],y=!0);y&&await c(s);let p=await u(),w=!1;for(let h of Object.keys(p))(h===t||h.startsWith(t+"/"))&&(delete p[h],w=!0);w&&await l(p)},async move(e,a){let t=m(e),s=m(a);await f(t,async()=>{if(await g.access(C(r(s))),await g.stat(r(s)).then(()=>!0,()=>!1))throw S(s);await g.rename(r(t),r(s))});let y=await i();d(y,t,s,!1)&&await c(y);let p=await u();d(p,t,s,!1)&&await l(p)},async copy(e,a){let t=m(e),s=m(a);await f(t,async()=>{if(await g.access(C(r(s))),await g.stat(r(s)).then(()=>!0,()=>!1))throw S(s);await g.cp(r(t),r(s),{recursive:!0})});let y=await i();d(y,t,s,!0)&&await c(y);let p=await u();d(p,t,s,!0)&&await l(p)},async getMeta(e){let a=m(e);return await f(a,()=>g.stat(r(a)).then(()=>{})),(await i())[a]??{}},async setMeta(e,a){let t=m(e);await f(t,()=>g.stat(r(t)).then(()=>{}));let s=await i();s[t]=a,await c(s)},watch(e,a){let t=m(e),s=(p,w)=>{if(!w)return;let h=m(t+"/"+w.toString());o(h)||g.stat(r(h)).then(()=>a({type:p==="change"?"update":"create",path:h}),()=>a({type:"remove",path:h}))},y;try{y=$(r(t),{recursive:!0},s)}catch{try{y=$(r(t),s)}catch{return()=>{}}}return()=>y.close()},async readStream(e,a){let t=m(e);await f(t,async()=>{if((await g.stat(r(t))).isDirectory())throw D(t)});let s=it(r(t),a?.signal?{signal:a.signal}:{});return st.toWeb(s)},async writeStream(e,a){let t=m(e),s=await f(t,async()=>(await g.access(C(r(t))),g.open(r(t),"w")));return new WritableStream({async write(y){try{await s.write(y)}catch(p){throw await s.close().catch(()=>{}),p}},close:async()=>{await s.close();let y=await u();if(y[t]=(y[t]??0)+1,await l(y),a?.meta){let p=await i();p[t]=a.meta,await c(p)}},abort:async()=>{await s.close()}})}}}var pt={streaming:!1,watch:!0,atomicMove:!1,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0},W=new Uint8Array(0);function ut(){let n=new Map,r=0;return{async get(o){let i=n.get(o);return i?{body:i.body.slice(),meta:{...i.meta},size:i.body.length,mtime:i.mtime,version:i.version}:null},async put(o,i,c){n.set(o,{body:i.slice(),meta:{...c},mtime:Date.now(),version:String(++r)})},async del(o){n.delete(o)},async head(o){let i=n.get(o);return i?{size:i.body.length,mtime:i.mtime,meta:{...i.meta},version:i.version}:null},async list(o){let i=[];for(let[c,u]of n)c.startsWith(o)&&i.push({key:c,size:u.body.length,mtime:u.mtime});return i}}}function mt(n){let r=n.client,o=n.prefix?m("/"+n.prefix).slice(1):"",i=e=>{let a=m(e).slice(1);return o?a?o+"/"+a:o:a},c=e=>i(e)+"/",u=e=>{let a=i(e);return a?a+"/":""},l=(e,a)=>a===e||a.startsWith(e==="/"?"/":e+"/"),d=async e=>m(e)==="/"?"dir":await r.head(i(e))?"file":await r.head(c(e))||(await r.list(u(e))).length?"dir":null,f=async e=>{let a=V(e),t=await d(a);if(t===null)throw v(a);if(t!=="dir")throw M(a)};return{capabilities:()=>pt,async read(e){let a=m(e),t=await r.get(i(a));if(!t)throw await d(a)==="dir"?D(a):v(a);return t.body.slice()},async write(e,a,t){let s=m(e);if(await d(s)==="dir")throw D(s);await f(s);let y=await r.head(i(s));if(t?.ifAbsent&&y)throw S(s);if(t?.ifMatch!==void 0&&(y?.version??"")!==t.ifMatch)throw F(s);await r.put(i(s),O(a),t?.meta??y?.meta??{})},async list(e,a){let t=m(e),s=await d(t);if(s===null)throw v(t);if(s!=="dir")throw M(t);let y=u(t),p=new Map;for(let w of await r.list(y)){let h=w.key.slice(y.length),T=h.endsWith("/");if(T&&(h=h.slice(0,-1)),h!=="")if(a?.recursive){let k=m(t+"/"+h),R=T?"dir":"file";(!p.has(k)||R==="dir")&&p.set(k,{name:h.split("/").pop(),path:k,type:R})}else{let k=h.split("/")[0],R=m(t+"/"+k),N=T||h.includes("/")?"dir":"file";(!p.has(R)||N==="dir")&&p.set(R,{name:k,path:R,type:N})}}return[...p.values()]},async stat(e){let a=m(e),t=await d(a);if(t===null)throw v(a);if(t==="file"){let y=await r.head(i(a));return{type:"file",size:y?.size??0,mtime:y?.mtime??0,ctime:y?.mtime??0,meta:y?.meta??{},version:y?.version}}let s=await r.head(c(a));return{type:"dir",size:0,mtime:s?.mtime??0,ctime:s?.mtime??0,meta:s?.meta??{}}},async exists(e){return await d(e)!==null},async mkdir(e,a){let t=m(e);if(await d(t)!==null){if(a?.recursive)return;throw S(t)}if(a?.recursive){let s=t.split("/").filter(Boolean),y="";for(let p of s){y+="/"+p;let w=await d(y);if(w==="file")throw M(y);w===null&&await r.put(c(y),W,{})}return}await f(t),await r.put(c(t),W,{})},async remove(e,a){let t=m(e),s=await d(t);if(s===null)throw v(t);if(s==="file"){await r.del(i(t));return}let y=await r.list(u(t));if(y.filter(w=>w.key!==c(t)).length&&!a?.recursive)throw x("directory not empty",t);for(let w of y)await r.del(w.key);await r.del(c(t))},async copy(e,a){let t=m(e),s=m(a);if(l(t,s))throw x("cannot copy into itself",s);let y=await d(t);if(y===null)throw v(t);if(await d(s)!==null)throw S(s);if(await f(s),y==="file"){let w=await r.get(i(t));w&&await r.put(i(s),w.body,w.meta);return}await r.put(c(s),W,{});let p=u(t);for(let w of await r.list(p)){let h=w.key.slice(p.length);if(h==="")continue;let T=u(s)+h;if(h.endsWith("/"))await r.put(T,W,{});else{let k=await r.get(w.key);k&&await r.put(T,k.body,k.meta)}}},async move(e,a){await this.copy(e,a),await this.remove(e,{recursive:!0})},async getMeta(e){let a=m(e),t=await d(a);if(t===null)throw v(a);return(await r.head(t==="file"?i(a):c(a)))?.meta??{}},async setMeta(e,a){let t=m(e),s=await d(t);if(s===null)throw v(t);if(s==="file"){let y=await r.get(i(t));await r.put(i(t),y?.body??W,a)}else await r.put(c(t),W,a)},watch(e,a){let t=m(e),s=u(t),y=i(t)?i(t)+"/":"",p=U=>m("/"+(o?U.slice(o.length+1):U)),w=new Map,h=!1,T=async()=>{let U=new Map;for(let E of await r.list(s))E.key!==y&&U.set(E.key,E.mtime);return U};T().then(U=>{w=U,h=!0});let k=!1,R=async()=>{if(!(!h||k)){k=!0;try{let U=await T();for(let[E,tt]of U)w.has(E)?w.get(E)!==tt&&a({type:"update",path:p(E)}):a({type:"create",path:p(E)});for(let E of w.keys())U.has(E)||a({type:"remove",path:p(E)});w=U}finally{k=!1}}},N=setInterval(()=>{R()},n.pollMs??200);return N.unref?.(),()=>clearInterval(N)}}}var j=new Uint8Array([86,75,1]),G=47,I=globalThis.crypto.subtle;function X(n){let r=new Uint8Array(n);return globalThis.crypto.getRandomValues(r),r}async function dt(n,r){let o=await I.importKey("raw",new TextEncoder().encode(n),"PBKDF2",!1,["deriveKey"]);return I.deriveKey({name:"PBKDF2",salt:r.slice(),iterations:21e4,hash:"SHA-256"},o,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}function ft(n,r){if(!r.key&&!r.passphrase)throw new Error("encrypt: key or passphrase required");let o=r.key?I.importKey("raw",r.key.slice(),"AES-GCM",!1,["encrypt","decrypt"]):null,i=u=>o??dt(r.passphrase,u),c={...n.capabilities(),streaming:!1,randomAccess:!1};return{...n,readStream:void 0,writeStream:void 0,capabilities:()=>c,async stat(u){let l=await n.stat(u);return l.type==="file"?{...l,size:Math.max(0,l.size-G)}:l},async write(u,l,d){let f=X(16),e=X(12),a=await i(f),t=new Uint8Array(await I.encrypt({name:"AES-GCM",iv:e},a,O(l).slice()));await n.write(u,L([j,f,e,t]),d)},async read(u,l){let d=await n.read(u,l);if(d.length<G||d[0]!==j[0]||d[1]!==j[1]||d[2]!==j[2])throw x("invalid ciphertext",u);let f=await i(d.slice(3,19)),e;try{e=await I.decrypt({name:"AES-GCM",iv:d.slice(19,31)},f,d.slice(31))}catch{throw x("decryption failed",u)}return new Uint8Array(e)}}}function lt(n,r={}){let o=r.store??new Map,i=r.ttlMs??0,c=()=>i?Date.now():0,u=()=>i?Date.now()+i:1/0,l=(f,e)=>e===f||e.startsWith(f==="/"?"/":f+"/"),d=f=>{for(let e of[...o.keys()])l(f,e)&&o.delete(e)};return{...n,readStream:void 0,writeStream:void 0,async read(f,e){let a=m(f),t=o.get(a);if(t&&t.exp>c())return t.data.slice();let s=await n.read(a,e);return o.set(a,{data:s.slice(),exp:u()}),s.slice()},async write(f,e,a){let t=m(f),s=O(e).slice();await n.write(t,s,a),o.set(t,{data:s,exp:u()})},async remove(f,e){let a=m(f);await n.remove(a,e),d(a)},async move(f,e){let a=m(f),t=m(e);await n.move(a,t),d(a),d(t)},async copy(f,e){let a=m(f),t=m(e);await n.copy(a,t),d(t)}}}var wt=new TextEncoder,ht=new TextDecoder,B=new Uint8Array(0);function A(n,r=B){let o=wt.encode(JSON.stringify(n)),i=new Uint8Array(4+o.length+r.length);return new DataView(i.buffer).setUint32(0,o.length),i.set(o,4),i.set(r,4+o.length),i}function H(n){let o=new DataView(n.buffer,n.byteOffset,n.byteLength).getUint32(0);return{header:JSON.parse(ht.decode(n.subarray(4,4+o))),data:n.subarray(4+o)}}function Q(n,r,o=[],i){return A({m:n,p:r,a:o},i)}function gt(n){let{header:r,data:o}=H(n);return{method:r.m,path:r.p,args:r.a??[],data:o}}function Z(n){let{header:r,data:o}=H(n);return r.ok?{ok:!0,value:r.v,data:o}:{ok:!1,data:B,code:r.c,message:r.e,path:r.p}}async function _(n,r){let{method:o,path:i,args:c,data:u}=gt(r);try{switch(o){case"read":return A({ok:!0},await n.read(i,c[0]));case"write":return await n.write(i,u,c[0]),A({ok:!0});case"list":return A({ok:!0,v:await n.list(i,c[0])});case"stat":return A({ok:!0,v:await n.stat(i)});case"exists":return A({ok:!0,v:await n.exists(i)});case"mkdir":return await n.mkdir(i,c[0]),A({ok:!0});case"remove":return await n.remove(i,c[0]),A({ok:!0});case"move":return await n.move(i,c[0]),A({ok:!0});case"copy":return await n.copy(i,c[0]),A({ok:!0});case"getMeta":return A({ok:!0,v:await n.getMeta(i)});case"setMeta":return await n.setMeta(i,c[0]),A({ok:!0});default:throw z(o)}}catch(l){let d=l instanceof b?l:x(String(l?.message??l),i);return A({ok:!1,c:d.code,e:d.message,p:d.path})}}function P(n,r,o=B){let i=new Uint8Array(5+o.length);return i[0]=n,new DataView(i.buffer).setUint32(1,r),i.set(o,5),i}function q(n){let r=new DataView(n.buffer,n.byteOffset,n.byteLength);return{type:n[0],id:r.getUint32(1),payload:n.subarray(5)}}var bt=new TextEncoder,kt=new TextDecoder;function vt(n){return{handle:r=>_(n,r),async fetch(r){let o=new Uint8Array(await r.arrayBuffer());return new Response(await _(n,o),{headers:{"content-type":"application/octet-stream"}})},socket(r){let o=new Map;return{async message(i){let{type:c,id:u,payload:l}=q(i);if(c===0)r(P(1,u,await _(n,l)));else if(c===2){let{path:d}=JSON.parse(kt.decode(l));o.set(u,n.watch(d,f=>r(P(4,u,bt.encode(JSON.stringify(f))))))}else c===3&&(o.get(u)?.(),o.delete(u))},close(){for(let i of o.values())i();o.clear()}}}}}function St(n){let r=n.request?n:n.transport,o=n.capabilities??{streaming:!1,watch:!!r.watch,atomicMove:!1,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0},i=async(c,u,l,d)=>{let f=l?[...l]:[];for(;f.length&&f[f.length-1]===void 0;)f.pop();let e=Z(await r.request(Q(c,u,f,d)));if(!e.ok)throw new b(e.code,e.message??"",e.path);return e};return{capabilities:()=>o,async read(c,u){return(await i("read",c,[u])).data},async write(c,u,l){await i("write",c,[l],O(u))},async list(c,u){return(await i("list",c,[u])).value},async stat(c){return(await i("stat",c)).value},async exists(c){return(await i("exists",c)).value},async mkdir(c,u){await i("mkdir",c,[u])},async remove(c,u){await i("remove",c,[u])},async move(c,u){await i("move",c,[u])},async copy(c,u){await i("copy",c,[u])},async getMeta(c){return(await i("getMeta",c)).value},async setMeta(c,u){await i("setMeta",c,[u])},watch(c,u){if(!r.watch)throw z("watch");return r.watch(c,u)}}}function xt(n,r){let o=r??((i,c)=>fetch(i,c));return{async request(i){let c=await o(n,{method:"POST",body:i});return new Uint8Array(await c.arrayBuffer())}}}var At=new TextEncoder,Ot=new TextDecoder;function Ut(n,r){let o=r?r():new WebSocket(n);try{o.binaryType="arraybuffer"}catch{}let i=new Map,c=new Map,u=0,l=o.readyState===1?Promise.resolve():new Promise(d=>{o.addEventListener?o.addEventListener("open",()=>d()):o.onopen=()=>d()});return o.onmessage=d=>{let{type:f,id:e,payload:a}=q(new Uint8Array(d.data));f===1?(i.get(e)?.(a),i.delete(e)):f===4&&c.get(e)?.(JSON.parse(Ot.decode(a)))},{async request(d){await l;let f=++u;return new Promise(e=>{i.set(f,e),o.send(P(0,f,d))})},watch(d,f){let e=++u;return c.set(e,f),l.then(()=>o.send(P(2,e,At.encode(JSON.stringify({path:d}))))),()=>{c.delete(e),l.then(()=>o.send(P(3,e,B)))}}}}export{Y as BRAND,b as VfsError,S as alreadyExists,Mt as basename,lt as cache,It as collect,L as concat,F as conflict,V as dirname,ft as encrypt,xt as httpTransport,x as io,D as isADirectory,Pt as isVfsError,Et as join,at as memory,ut as memoryS3,yt as nodeFs,m as normalize,M as notADirectory,v as notFound,Rt as permissionDenied,Vt as readStream,St as remote,mt as s3,Dt as segments,vt as serve,O as toBytes,Ct as toText,z as unsupported,Lt as writeStream,Ut as wsTransport};
|
|
1
|
+
function u(i){let e=[];for(let p of i.split("/"))if(!(p===""||p===".")){if(p===".."){e.pop();continue}e.push(p)}return"/"+e.join("/")}function It(...i){return u(i.join("/"))}function F(i){let e=u(i),p=e.lastIndexOf("/");return p<=0?"/":e.slice(0,p)}function Vt(i){let e=u(i);return e.slice(e.lastIndexOf("/")+1)}function Bt(i){return u(i).split("/").filter(Boolean)}var Y=Symbol.for("vfskit.VfsError"),A=class extends Error{code;path;[Y]=!0;constructor(e,p,c){super(p),this.name="VfsError",this.code=e,this.path=c}},S=i=>new A("NOT_FOUND",`not found: ${i}`,i),k=i=>new A("ALREADY_EXISTS",`already exists: ${i}`,i),O=i=>new A("NOT_A_DIRECTORY",`not a directory: ${i}`,i),E=i=>new A("IS_A_DIRECTORY",`is a directory: ${i}`,i),Kt=i=>new A("PERMISSION_DENIED",`permission denied: ${i}`,i),z=i=>new A("UNSUPPORTED",`unsupported: ${i}`),C=i=>new A("CONFLICT",`version conflict: ${i}`,i),x=(i,e)=>new A("IO",i,e);function qt(i){return typeof i=="object"&&i!==null&&i[Y]===!0}var rt=new TextEncoder,nt=new TextDecoder;function U(i){return typeof i=="string"?rt.encode(i):i instanceof Uint8Array?i:new Uint8Array(i)}function jt(i){return nt.decode(i)}function I(i){let e=0;for(let f of i)e+=f.length;let p=new Uint8Array(e),c=0;for(let f of i)p.set(f,c),c+=f.length;return p}async function Gt(i,e,p){if(i.readStream)return i.readStream(e,p);let c=await i.read(e,p);return new ReadableStream({start(f){f.enqueue(c),f.close()}})}async function Xt(i,e,p){if(i.writeStream)return i.writeStream(e,p);let c=[];return new WritableStream({write(f){c.push(f)},async close(){await i.write(e,I(c),p)}})}async function $t(i){let e=[],p=i.getReader();for(;;){let{done:c,value:f}=await p.read();if(c)break;f&&e.push(f)}return I(e)}var it={streaming:!1,watch:!0,atomicMove:!0,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0};function at(){let i=()=>Date.now(),e=0,p=()=>String(++e),c=new Map([["/",{type:"dir",meta:{},mtime:i(),ctime:i()}]]),f=new Set,d=(n,s)=>s===n||s.startsWith(n==="/"?"/":n+"/"),h=(n,s)=>{for(let t of f)d(t.base,s)&&t.cb({type:n,path:s})},m=n=>{let s=c.get(n);if(!s)throw S(n);return s},w=n=>{let s=F(n),t=c.get(s);if(!t)throw S(s);if(t.type!=="dir")throw O(s)};return{capabilities:()=>it,async read(n){let s=u(n),t=m(s);if(t.type==="dir")throw E(s);return t.data.slice()},async write(n,s,t){let r=u(n);w(r);let a=c.get(r);if(a&&a.type==="dir")throw E(r);if(t?.ifAbsent&&a)throw k(r);if(t?.ifMatch!==void 0&&a?.version!==t.ifMatch)throw C(r);let o=a?a.ctime:i();c.set(r,{type:"file",data:U(s).slice(),meta:t?.meta?{...t.meta}:a?.meta??{},ctime:o,mtime:i(),version:p()}),h(a?"update":"create",r)},async list(n,s){let t=u(n);if(m(t).type!=="dir")throw O(t);let a=[];for(let[o,y]of c)o===t||!d(t,o)||!s?.recursive&&F(o)!==t||a.push({name:o.slice(o.lastIndexOf("/")+1),path:o,type:y.type});return a},async stat(n){let s=u(n),t=m(s);return{type:t.type,size:t.type==="file"?t.data.length:0,mtime:t.mtime,ctime:t.ctime,meta:{...t.meta},version:t.type==="file"?t.version:void 0}},async exists(n){return c.has(u(n))},async mkdir(n,s){let t=u(n);if(c.has(t)){if(s?.recursive)return;throw k(t)}if(s?.recursive){let r="";for(let a of t.split("/").filter(Boolean)){r+="/"+a;let o=c.get(r);if(o){if(o.type!=="dir")throw O(r);continue}c.set(r,{type:"dir",meta:{},mtime:i(),ctime:i()}),h("create",r)}return}w(t),c.set(t,{type:"dir",meta:{},mtime:i(),ctime:i()}),h("create",t)},async remove(n,s){let t=u(n);m(t);let r=[...c.keys()].filter(a=>a!==t&&d(t,a));if(r.length&&!s?.recursive)throw x("directory not empty",t);for(let a of[...r,t])c.delete(a),h("remove",a)},async move(n,s){let t=u(n),r=u(s);if(m(t),w(r),c.has(r))throw k(r);if(d(t,r))throw x("cannot move into itself",r);for(let a of[...c.keys()].filter(o=>o===t||d(t,o))){let o=c.get(a);c.delete(a),c.set(r+a.slice(t.length),o)}h("remove",t),h("create",r)},async copy(n,s){let t=u(n),r=u(s);if(m(t),w(r),c.has(r))throw k(r);for(let a of[...c.keys()].filter(o=>o===t||d(t,o))){let o=c.get(a);c.set(r+a.slice(t.length),o.type==="file"?{type:"file",data:o.data.slice(),meta:{...o.meta},mtime:o.mtime,ctime:o.ctime,version:p()}:{type:"dir",meta:{...o.meta},mtime:o.mtime,ctime:o.ctime})}h("create",r)},async getMeta(n){return{...m(u(n)).meta}},async setMeta(n,s){let t=m(u(n));t.meta={...s},t.mtime=i()},watch(n,s){let t={base:u(n),cb:s};return f.add(t),()=>{f.delete(t)}}}}import{promises as b,watch as G,createReadStream as st}from"fs";import{Readable as ot}from"stream";import{join as ct,dirname as N}from"path";var pt={streaming:!0,watch:!0,atomicMove:!0,nativeMeta:!1,randomAccess:!1,conditionalWrite:!0},j="/.vfskit/meta.json",_="/.vfskit/ver.json";function yt(i){let e=n=>ct(i,u(n)),p=n=>n==="/.vfskit"||n.startsWith("/.vfskit/"),c=async()=>{try{return JSON.parse(new TextDecoder().decode(await b.readFile(e(j))))}catch{return{}}},f=async n=>{await b.mkdir(N(e(j)),{recursive:!0}),await b.writeFile(e(j),new TextEncoder().encode(JSON.stringify(n)))},d=async()=>{try{return JSON.parse(new TextDecoder().decode(await b.readFile(e(_))))}catch{return{}}},h=async n=>{await b.mkdir(N(e(_)),{recursive:!0}),await b.writeFile(e(_),new TextEncoder().encode(JSON.stringify(n)))},m=(n,s,t,r)=>{let a=!1;for(let o of Object.keys(n))(o===s||o.startsWith(s+"/"))&&(n[t+o.slice(s.length)]=n[o],r||delete n[o],a=!0);return a},w=async(n,s)=>{try{return await s()}catch(t){if(t instanceof A)throw t;let r=t.code;throw r==="ENOENT"?S(n):r==="EEXIST"?k(n):r==="ENOTDIR"?O(n):r==="EISDIR"?E(n):x(String(t.message??t),n)}};return{capabilities:()=>pt,async read(n){let s=u(n);return w(s,async()=>{if((await b.stat(e(s))).isDirectory())throw E(s);return new Uint8Array(await b.readFile(e(s)))})},async write(n,s,t){let r=u(n);await w(r,()=>b.access(N(e(r))).then(()=>{}));let a=await d();if(t?.ifAbsent||t?.ifMatch!==void 0){let o=await b.stat(e(r)).then(()=>!0,()=>!1);if(t.ifAbsent&&o)throw k(r);if(t.ifMatch!==void 0&&String(a[r]??"")!==t.ifMatch)throw C(r)}if(await w(r,()=>b.writeFile(e(r),U(s)).then(()=>{})),a[r]=(a[r]??0)+1,await h(a),t?.meta){let o=await c();o[r]=t.meta,await f(o)}},async list(n,s){let t=u(n),r=[],a=async o=>{for(let y of await b.readdir(e(o),{withFileTypes:!0})){let l=u(o+"/"+y.name);p(l)||(r.push({name:y.name,path:l,type:y.isDirectory()?"dir":"file"}),s?.recursive&&y.isDirectory()&&await a(l))}};return await w(t,async()=>{if(!(await b.stat(e(t))).isDirectory())throw O(t);await a(t)}),r},async stat(n){let s=u(n);return w(s,async()=>{let t=await b.stat(e(s)),r=t.isDirectory(),a=r?void 0:(await d())[s];return{type:r?"dir":"file",size:r?0:t.size,mtime:t.mtimeMs,ctime:t.ctimeMs,meta:(await c())[s]??{},version:a!=null?String(a):void 0}})},async exists(n){try{return await b.stat(e(u(n))),!0}catch{return!1}},async mkdir(n,s){let t=u(n);await w(t,()=>b.mkdir(e(t),{recursive:s?.recursive}).then(()=>{}))},async remove(n,s){let t=u(n);await w(t,async()=>{if((await b.stat(e(t))).isDirectory()){if((await b.readdir(e(t))).length&&!s?.recursive)throw x("directory not empty",t);await b.rm(e(t),{recursive:!0,force:!0})}else await b.rm(e(t))});let r=await c(),a=!1;for(let l of Object.keys(r))(l===t||l.startsWith(t+"/"))&&(delete r[l],a=!0);a&&await f(r);let o=await d(),y=!1;for(let l of Object.keys(o))(l===t||l.startsWith(t+"/"))&&(delete o[l],y=!0);y&&await h(o)},async move(n,s){let t=u(n),r=u(s);await w(t,async()=>{if(await b.access(N(e(r))),await b.stat(e(r)).then(()=>!0,()=>!1))throw k(r);await b.rename(e(t),e(r))});let a=await c();m(a,t,r,!1)&&await f(a);let o=await d();m(o,t,r,!1)&&await h(o)},async copy(n,s){let t=u(n),r=u(s);await w(t,async()=>{if(await b.access(N(e(r))),await b.stat(e(r)).then(()=>!0,()=>!1))throw k(r);await b.cp(e(t),e(r),{recursive:!0})});let a=await c();m(a,t,r,!0)&&await f(a);let o=await d();m(o,t,r,!0)&&await h(o)},async getMeta(n){let s=u(n);return await w(s,()=>b.stat(e(s)).then(()=>{})),(await c())[s]??{}},async setMeta(n,s){let t=u(n);await w(t,()=>b.stat(e(t)).then(()=>{}));let r=await c();r[t]=s,await f(r)},watch(n,s){let t=u(n),r=(o,y)=>{if(!y)return;let l=u(t+"/"+y.toString());p(l)||b.stat(e(l)).then(()=>s({type:o==="change"?"update":"create",path:l}),()=>s({type:"remove",path:l}))},a;try{a=G(e(t),{recursive:!0},r)}catch{try{a=G(e(t),r)}catch{return()=>{}}}return()=>a.close()},async readStream(n,s){let t=u(n);await w(t,async()=>{if((await b.stat(e(t))).isDirectory())throw E(t)});let r=st(e(t),s?.signal?{signal:s.signal}:{});return ot.toWeb(r)},async writeStream(n,s){let t=u(n),r=await w(t,async()=>(await b.access(N(e(t))),b.open(e(t),"w")));return new WritableStream({async write(a){try{await r.write(a)}catch(o){throw await r.close().catch(()=>{}),o}},close:async()=>{await r.close();let a=await d();if(a[t]=(a[t]??0)+1,await h(a),s?.meta){let o=await c();o[t]=s.meta,await f(o)}},abort:async()=>{await r.close()}})}}}var ft={streaming:!1,watch:!0,atomicMove:!1,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0},L=new Uint8Array(0);function ut(){let i=new Map,e=0;return{async get(p){let c=i.get(p);return c?{body:c.body.slice(),meta:{...c.meta},size:c.body.length,mtime:c.mtime,version:c.version}:null},async put(p,c,f){i.set(p,{body:c.slice(),meta:{...f},mtime:Date.now(),version:String(++e)})},async del(p){i.delete(p)},async head(p){let c=i.get(p);return c?{size:c.body.length,mtime:c.mtime,meta:{...c.meta},version:c.version}:null},async list(p){let c=[];for(let[f,d]of i)f.startsWith(p)&&c.push({key:f,size:d.body.length,mtime:d.mtime});return c}}}function lt(i){let e=i.client,p=i.prefix?u("/"+i.prefix).slice(1):"",c=n=>{let s=u(n).slice(1);return p?s?p+"/"+s:p:s},f=n=>c(n)+"/",d=n=>{let s=c(n);return s?s+"/":""},h=(n,s)=>s===n||s.startsWith(n==="/"?"/":n+"/"),m=async n=>u(n)==="/"?"dir":await e.head(c(n))?"file":await e.head(f(n))||(await e.list(d(n))).length?"dir":null,w=async n=>{let s=F(n),t=await m(s);if(t===null)throw S(s);if(t!=="dir")throw O(s)};return{capabilities:()=>ft,async read(n){let s=u(n),t=await e.get(c(s));if(!t)throw await m(s)==="dir"?E(s):S(s);return t.body.slice()},async write(n,s,t){let r=u(n);if(await m(r)==="dir")throw E(r);await w(r);let a=await e.head(c(r));if(t?.ifAbsent&&a)throw k(r);if(t?.ifMatch!==void 0&&(a?.version??"")!==t.ifMatch)throw C(r);await e.put(c(r),U(s),t?.meta??a?.meta??{})},async list(n,s){let t=u(n),r=await m(t);if(r===null)throw S(t);if(r!=="dir")throw O(t);let a=d(t),o=new Map;for(let y of await e.list(a)){let l=y.key.slice(a.length),g=l.endsWith("/");if(g&&(l=l.slice(0,-1)),l!=="")if(s?.recursive){let v=u(t+"/"+l),T=g?"dir":"file";(!o.has(v)||T==="dir")&&o.set(v,{name:l.split("/").pop(),path:v,type:T})}else{let v=l.split("/")[0],T=u(t+"/"+v),W=g||l.includes("/")?"dir":"file";(!o.has(T)||W==="dir")&&o.set(T,{name:v,path:T,type:W})}}return[...o.values()]},async stat(n){let s=u(n),t=await m(s);if(t===null)throw S(s);if(t==="file"){let a=await e.head(c(s));return{type:"file",size:a?.size??0,mtime:a?.mtime??0,ctime:a?.mtime??0,meta:a?.meta??{},version:a?.version}}let r=await e.head(f(s));return{type:"dir",size:0,mtime:r?.mtime??0,ctime:r?.mtime??0,meta:r?.meta??{}}},async exists(n){return await m(n)!==null},async mkdir(n,s){let t=u(n);if(await m(t)!==null){if(s?.recursive)return;throw k(t)}if(s?.recursive){let r=t.split("/").filter(Boolean),a="";for(let o of r){a+="/"+o;let y=await m(a);if(y==="file")throw O(a);y===null&&await e.put(f(a),L,{})}return}await w(t),await e.put(f(t),L,{})},async remove(n,s){let t=u(n),r=await m(t);if(r===null)throw S(t);if(r==="file"){await e.del(c(t));return}let a=await e.list(d(t));if(a.filter(y=>y.key!==f(t)).length&&!s?.recursive)throw x("directory not empty",t);for(let y of a)await e.del(y.key);await e.del(f(t))},async copy(n,s){let t=u(n),r=u(s);if(h(t,r))throw x("cannot copy into itself",r);let a=await m(t);if(a===null)throw S(t);if(await m(r)!==null)throw k(r);if(await w(r),a==="file"){let y=await e.get(c(t));y&&await e.put(c(r),y.body,y.meta);return}await e.put(f(r),L,{});let o=d(t);for(let y of await e.list(o)){let l=y.key.slice(o.length);if(l==="")continue;let g=d(r)+l;if(l.endsWith("/"))await e.put(g,L,{});else{let v=await e.get(y.key);v&&await e.put(g,v.body,v.meta)}}},async move(n,s){await this.copy(n,s),await this.remove(n,{recursive:!0})},async getMeta(n){let s=u(n),t=await m(s);if(t===null)throw S(s);return(await e.head(t==="file"?c(s):f(s)))?.meta??{}},async setMeta(n,s){let t=u(n),r=await m(t);if(r===null)throw S(t);if(r==="file"){let a=await e.get(c(t));await e.put(c(t),a?.body??L,s)}else await e.put(f(t),L,s)},watch(n,s){let t=u(n),r=d(t),a=c(t)?c(t)+"/":"",o=R=>u("/"+(p?R.slice(p.length+1):R)),y=new Map,l=!1,g=async()=>{let R=new Map;for(let D of await e.list(r))D.key!==a&&R.set(D.key,D.mtime);return R};g().then(R=>{y=R,l=!0});let v=!1,T=async()=>{if(!(!l||v)){v=!0;try{let R=await g();for(let[D,et]of R)y.has(D)?y.get(D)!==et&&s({type:"update",path:o(D)}):s({type:"create",path:o(D)});for(let D of y.keys())R.has(D)||s({type:"remove",path:o(D)});y=R}finally{v=!1}}},W=setInterval(()=>{T()},i.pollMs??200);return W.unref?.(),()=>clearInterval(W)}}}import{createRequire as dt}from"module";var{DatabaseSync:mt}=dt(import.meta.url)("node:sqlite"),wt={streaming:!1,watch:!1,atomicMove:!1,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0},ht=i=>i.replace(/[\\%_]/g,e=>"\\"+e);function gt(i){let e=new mt(i);e.exec("CREATE TABLE IF NOT EXISTS files (path TEXT PRIMARY KEY, type TEXT NOT NULL, data BLOB, meta TEXT NOT NULL DEFAULT '{}', version INTEGER NOT NULL DEFAULT 0)"),e.prepare("INSERT OR IGNORE INTO files (path, type) VALUES ('/', 'dir')").run();let p=e.prepare("SELECT path, type, data, meta, version FROM files WHERE path = ?"),c=e.prepare("INSERT INTO files (path, type, data, meta, version) VALUES (?, ?, ?, ?, ?) ON CONFLICT(path) DO UPDATE SET type=excluded.type, data=excluded.data, meta=excluded.meta, version=excluded.version"),f=e.prepare("DELETE FROM files WHERE path = ?"),d=e.prepare("SELECT path, type FROM files WHERE path LIKE ? ESCAPE '\\'"),h=a=>p.get(a),m=(a,o,y,l,g)=>c.run(a,o,y,JSON.stringify(l),g),w=(a,o)=>o===a||o.startsWith(a==="/"?"/":a+"/"),n=a=>d.all(ht(a==="/"?"/":a+"/")+"%").filter(o=>w(a,o.path)&&o.path!==a),s=a=>{let o=h(a);if(!o)throw S(a);return o},t=a=>{let o=F(a),y=h(o);if(!y)throw S(o);if(y.type!=="dir")throw O(o)},r=a=>a.data?new Uint8Array(a.data):new Uint8Array(0);return{capabilities:()=>wt,async read(a){let o=u(a),y=s(o);if(y.type==="dir")throw E(o);return r(y)},async write(a,o,y){let l=u(a);t(l);let g=h(l);if(g?.type==="dir")throw E(l);if(y?.ifAbsent&&g)throw k(l);if(y?.ifMatch!==void 0&&String(g?.version??"")!==y.ifMatch)throw C(l);let v=y?.meta??(g?JSON.parse(g.meta):{});m(l,"file",U(o),v,(g?.version??0)+1)},async list(a,o){let y=u(a);if(s(y).type!=="dir")throw O(y);let g=[];for(let v of n(y))!o?.recursive&&F(v.path)!==y||g.push({name:v.path.slice(v.path.lastIndexOf("/")+1),path:v.path,type:v.type});return g},async stat(a){let o=u(a),y=s(o);return{type:y.type,size:y.type==="file"?r(y).length:0,mtime:0,ctime:0,meta:JSON.parse(y.meta),version:y.type==="file"?String(y.version):void 0}},async exists(a){return!!h(u(a))},async mkdir(a,o){let y=u(a);if(h(y)){if(o?.recursive)return;throw k(y)}if(o?.recursive){let g="";for(let v of y.split("/").filter(Boolean)){g+="/"+v;let T=h(g);if(T?.type==="file")throw O(g);T||m(g,"dir",null,{},0)}return}t(y),m(y,"dir",null,{},0)},async remove(a,o){let y=u(a);s(y);let l=n(y);if(l.length&&!o?.recursive)throw x("directory not empty",y);for(let g of l)f.run(g.path);f.run(y)},async move(a,o){await this.copy(a,o),await this.remove(a,{recursive:!0})},async copy(a,o){let y=u(a),l=u(o);if(s(y),t(l),h(l))throw k(l);if(w(y,l))throw x("cannot copy into itself",l);for(let g of[y,...n(y).map(v=>v.path)]){let v=s(g);m(l+g.slice(y.length),v.type,v.data?new Uint8Array(v.data):null,JSON.parse(v.meta),(v.version??0)+1)}},async getMeta(a){return JSON.parse(s(u(a)).meta)},async setMeta(a,o){let y=u(a),l=s(y);m(y,l.type,l.data?new Uint8Array(l.data):null,o,l.version)},watch(){return()=>{}}}}var vt={streaming:!1,watch:!1,atomicMove:!1,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0};function bt(i){let e="";for(let p=0;p<i.length;p+=32768)e+=String.fromCharCode(...i.subarray(p,p+32768));return btoa(e)}function X(i){let e=atob(i),p=new Uint8Array(e.length);for(let c=0;c<e.length;c++)p[c]=e.charCodeAt(c);return p}function kt(){let i=new Map;return{async get(e){return i.get(e)},async set(e,p){i.set(e,p)},async delete(e){i.delete(e)},async list(e){return[...i.keys()].filter(p=>p.startsWith(e))}}}function St(i=globalThis.localStorage){return{async get(e){return i.getItem(e)},async set(e,p){i.setItem(e,p)},async delete(e){i.removeItem(e)},async list(e){let p=[];for(let c=0;c<i.length;c++){let f=i.key(c);f&&f.startsWith(e)&&p.push(f)}return p}}}function xt(i){let e=i.store,p=(i.prefix?i.prefix+":":"")+"vfs:",c=r=>p+u(r),f=r=>r.slice(p.length),d=(r,a)=>a===r||a.startsWith(r==="/"?"/":r+"/"),h=async r=>{let a=await e.get(c(r));return a?JSON.parse(a):void 0},m=(r,a)=>e.set(c(r),JSON.stringify(a)),w=async r=>{let a=await h(r);if(!a)throw S(r);return a},n=async r=>(await e.list(c(r)===p+"/"?p+"/":c(r)+"/")).map(f).filter(a=>a!==r&&d(r,a)),s=async r=>{let a=F(r),o=await h(a);if(!o)throw S(a);if(o.t!=="dir")throw O(a)},t=(async()=>{await e.get(c("/"))||await m("/",{t:"dir",m:{},v:0})})();return{capabilities:()=>vt,async read(r){await t;let a=u(r),o=await w(a);if(o.t==="dir")throw E(a);return X(o.d??"")},async write(r,a,o){await t;let y=u(r);await s(y);let l=await h(y);if(l?.t==="dir")throw E(y);if(o?.ifAbsent&&l)throw k(y);if(o?.ifMatch!==void 0&&String(l?.v??"")!==o.ifMatch)throw C(y);await m(y,{t:"file",d:bt(U(a)),m:o?.meta??l?.m??{},v:(l?.v??0)+1})},async list(r,a){await t;let o=u(r);if((await w(o)).t!=="dir")throw O(o);let l=[];for(let g of await n(o)){if(!a?.recursive&&F(g)!==o)continue;let v=await h(g);v&&l.push({name:g.slice(g.lastIndexOf("/")+1),path:g,type:v.t})}return l},async stat(r){await t;let a=u(r),o=await w(a);return{type:o.t,size:o.t==="file"?X(o.d??"").length:0,mtime:0,ctime:0,meta:{...o.m},version:o.t==="file"?String(o.v):void 0}},async exists(r){return await t,!!await h(u(r))},async mkdir(r,a){await t;let o=u(r);if(await h(o)){if(a?.recursive)return;throw k(o)}if(a?.recursive){let l="";for(let g of o.split("/").filter(Boolean)){l+="/"+g;let v=await h(l);if(v?.t==="file")throw O(l);v||await m(l,{t:"dir",m:{},v:0})}return}await s(o),await m(o,{t:"dir",m:{},v:0})},async remove(r,a){await t;let o=u(r);await w(o);let y=await n(o);if(y.length&&!a?.recursive)throw x("directory not empty",o);for(let l of[...y,o])await e.delete(c(l))},async move(r,a){await this.copy(r,a),await this.remove(r,{recursive:!0})},async copy(r,a){await t;let o=u(r),y=u(a);if(await w(o),await s(y),await h(y))throw k(y);if(d(o,y))throw x("cannot copy into itself",y);for(let l of[o,...await n(o)]){let g=await h(l);g&&await m(y+l.slice(o.length),{...g,v:(g.v??0)+1})}},async getMeta(r){return await t,{...(await w(u(r))).m}},async setMeta(r,a){await t;let o=u(r),y=await w(o);await m(o,{...y,m:{...a}})},watch(){return()=>{}}}}var K=new Uint8Array([86,75,1]),$=47,V=globalThis.crypto.subtle;function H(i){let e=new Uint8Array(i);return globalThis.crypto.getRandomValues(e),e}async function Ot(i,e){let p=await V.importKey("raw",new TextEncoder().encode(i),"PBKDF2",!1,["deriveKey"]);return V.deriveKey({name:"PBKDF2",salt:e.slice(),iterations:21e4,hash:"SHA-256"},p,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}function At(i,e){if(!e.key&&!e.passphrase)throw new Error("encrypt: key or passphrase required");let p=e.key?V.importKey("raw",e.key.slice(),"AES-GCM",!1,["encrypt","decrypt"]):null,c=d=>p??Ot(e.passphrase,d),f={...i.capabilities(),streaming:!1,randomAccess:!1};return{...i,readStream:void 0,writeStream:void 0,capabilities:()=>f,async stat(d){let h=await i.stat(d);return h.type==="file"?{...h,size:Math.max(0,h.size-$)}:h},async write(d,h,m){let w=H(16),n=H(12),s=await c(w),t=new Uint8Array(await V.encrypt({name:"AES-GCM",iv:n},s,U(h).slice()));await i.write(d,I([K,w,n,t]),m)},async read(d,h){let m=await i.read(d,h);if(m.length<$||m[0]!==K[0]||m[1]!==K[1]||m[2]!==K[2])throw x("invalid ciphertext",d);let w=await c(m.slice(3,19)),n;try{n=await V.decrypt({name:"AES-GCM",iv:m.slice(19,31)},w,m.slice(31))}catch{throw x("decryption failed",d)}return new Uint8Array(n)}}}function Et(i,e={}){let p=e.store??new Map,c=e.ttlMs??0,f=()=>c?Date.now():0,d=()=>c?Date.now()+c:1/0,h=(w,n)=>n===w||n.startsWith(w==="/"?"/":w+"/"),m=w=>{for(let n of[...p.keys()])h(w,n)&&p.delete(n)};return{...i,readStream:void 0,writeStream:void 0,async read(w,n){let s=u(w),t=p.get(s);if(t&&t.exp>f())return t.data.slice();let r=await i.read(s,n);return p.set(s,{data:r.slice(),exp:d()}),r.slice()},async write(w,n,s){let t=u(w),r=U(n).slice();await i.write(t,r,s),p.set(t,{data:r,exp:d()})},async remove(w,n){let s=u(w);await i.remove(s,n),m(s)},async move(w,n){let s=u(w),t=u(n);await i.move(s,t),m(s),m(t)},async copy(w,n){let s=u(w),t=u(n);await i.copy(s,t),m(t)}}}var Ut=new TextEncoder,Mt=new TextDecoder,B=new Uint8Array(0);function M(i,e=B){let p=Ut.encode(JSON.stringify(i)),c=new Uint8Array(4+p.length+e.length);return new DataView(c.buffer).setUint32(0,p.length),c.set(p,4),c.set(e,4+p.length),c}function Q(i){let p=new DataView(i.buffer,i.byteOffset,i.byteLength).getUint32(0);return{header:JSON.parse(Mt.decode(i.subarray(4,4+p))),data:i.subarray(4+p)}}function Z(i,e,p=[],c){return M({m:i,p:e,a:p},c)}function Tt(i){let{header:e,data:p}=Q(i);return{method:e.m,path:e.p,args:e.a??[],data:p}}function tt(i){let{header:e,data:p}=Q(i);return e.ok?{ok:!0,value:e.v,data:p}:{ok:!1,data:B,code:e.c,message:e.e,path:e.p}}async function q(i,e){let{method:p,path:c,args:f,data:d}=Tt(e);try{switch(p){case"read":return M({ok:!0},await i.read(c,f[0]));case"write":return await i.write(c,d,f[0]),M({ok:!0});case"list":return M({ok:!0,v:await i.list(c,f[0])});case"stat":return M({ok:!0,v:await i.stat(c)});case"exists":return M({ok:!0,v:await i.exists(c)});case"mkdir":return await i.mkdir(c,f[0]),M({ok:!0});case"remove":return await i.remove(c,f[0]),M({ok:!0});case"move":return await i.move(c,f[0]),M({ok:!0});case"copy":return await i.copy(c,f[0]),M({ok:!0});case"getMeta":return M({ok:!0,v:await i.getMeta(c)});case"setMeta":return await i.setMeta(c,f[0]),M({ok:!0});default:throw z(p)}}catch(h){let m=h instanceof A?h:x(String(h?.message??h),c);return M({ok:!1,c:m.code,e:m.message,p:m.path})}}function P(i,e,p=B){let c=new Uint8Array(5+p.length);return c[0]=i,new DataView(c.buffer).setUint32(1,e),c.set(p,5),c}function J(i){let e=new DataView(i.buffer,i.byteOffset,i.byteLength);return{type:i[0],id:e.getUint32(1),payload:i.subarray(5)}}var Rt=new TextEncoder,Dt=new TextDecoder;function Ft(i){return{handle:e=>q(i,e),async fetch(e){let p=new Uint8Array(await e.arrayBuffer());return new Response(await q(i,p),{headers:{"content-type":"application/octet-stream"}})},socket(e){let p=new Map;return{async message(c){let{type:f,id:d,payload:h}=J(c);if(f===0)e(P(1,d,await q(i,h)));else if(f===2){let{path:m}=JSON.parse(Dt.decode(h));p.set(d,i.watch(m,w=>e(P(4,d,Rt.encode(JSON.stringify(w))))))}else f===3&&(p.get(d)?.(),p.delete(d))},close(){for(let c of p.values())c();p.clear()}}}}}function Ct(i){let e=i.request?i:i.transport,p=i.capabilities??{streaming:!1,watch:!!e.watch,atomicMove:!1,nativeMeta:!0,randomAccess:!1,conditionalWrite:!0},c=async(f,d,h,m)=>{let w=h?[...h]:[];for(;w.length&&w[w.length-1]===void 0;)w.pop();let n=tt(await e.request(Z(f,d,w,m)));if(!n.ok)throw new A(n.code,n.message??"",n.path);return n};return{capabilities:()=>p,async read(f,d){return(await c("read",f,[d])).data},async write(f,d,h){await c("write",f,[h],U(d))},async list(f,d){return(await c("list",f,[d])).value},async stat(f){return(await c("stat",f)).value},async exists(f){return(await c("exists",f)).value},async mkdir(f,d){await c("mkdir",f,[d])},async remove(f,d){await c("remove",f,[d])},async move(f,d){await c("move",f,[d])},async copy(f,d){await c("copy",f,[d])},async getMeta(f){return(await c("getMeta",f)).value},async setMeta(f,d){await c("setMeta",f,[d])},watch(f,d){if(!e.watch)throw z("watch");return e.watch(f,d)}}}function Pt(i,e){let p=e??((c,f)=>fetch(c,f));return{async request(c){let f=await p(i,{method:"POST",body:c});return new Uint8Array(await f.arrayBuffer())}}}var Nt=new TextEncoder,Lt=new TextDecoder;function Wt(i,e){let p=e?e():new WebSocket(i);try{p.binaryType="arraybuffer"}catch{}let c=new Map,f=new Map,d=0,h=p.readyState===1?Promise.resolve():new Promise(m=>{p.addEventListener?p.addEventListener("open",()=>m()):p.onopen=()=>m()});return p.onmessage=m=>{let{type:w,id:n,payload:s}=J(new Uint8Array(m.data));w===1?(c.get(n)?.(s),c.delete(n)):w===4&&f.get(n)?.(JSON.parse(Lt.decode(s)))},{async request(m){await h;let w=++d;return new Promise(n=>{c.set(w,n),p.send(P(0,w,m))})},watch(m,w){let n=++d;return f.set(n,w),h.then(()=>p.send(P(2,n,Nt.encode(JSON.stringify({path:m}))))),()=>{f.delete(n),h.then(()=>p.send(P(3,n,B)))}}}}export{Y as BRAND,A as VfsError,k as alreadyExists,Vt as basename,Et as cache,$t as collect,I as concat,C as conflict,F as dirname,At as encrypt,Pt as httpTransport,x as io,E as isADirectory,qt as isVfsError,It as join,xt as kv,St as localStorageKv,kt as memKv,at as memory,ut as memoryS3,yt as nodeFs,u as normalize,O as notADirectory,S as notFound,Kt as permissionDenied,Gt as readStream,Ct as remote,lt as s3,Bt as segments,Ft as serve,gt as sqlite,U as toBytes,jt as toText,z as unsupported,Xt as writeStream,Wt as wsTransport};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artemjs/vfskit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Universal abstraction over any virtual file system - memory, disk, S3 and more - with composable adapters, encryption and a remote bridge. Backend kit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vfs",
|
|
@@ -55,7 +55,9 @@
|
|
|
55
55
|
"@vfskit/transport-http": "*",
|
|
56
56
|
"@vfskit/transport-ws": "*",
|
|
57
57
|
"tsup": "^8.3.0",
|
|
58
|
-
"@vfskit/cache": "*"
|
|
58
|
+
"@vfskit/cache": "*",
|
|
59
|
+
"@vfskit/kv": "*",
|
|
60
|
+
"@vfskit/sqlite": "*"
|
|
59
61
|
},
|
|
60
62
|
"publishConfig": {
|
|
61
63
|
"access": "public"
|