alchemy 0.76.0 → 0.77.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/bin/alchemy.js +111 -30
- package/bin/services/execute-alchemy.ts +10 -1
- package/lib/aws/ec2/index.d.ts +1 -0
- package/lib/aws/ec2/index.d.ts.map +1 -1
- package/lib/aws/ec2/index.js +1 -0
- package/lib/aws/ec2/index.js.map +1 -1
- package/lib/aws/index.d.ts +1 -7
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +1 -8
- package/lib/aws/index.js.map +1 -1
- package/lib/cloudflare/auth.d.ts.map +1 -1
- package/lib/cloudflare/auth.js +1 -0
- package/lib/cloudflare/auth.js.map +1 -1
- package/lib/cloudflare/bucket-custom-domain.d.ts +90 -0
- package/lib/cloudflare/bucket-custom-domain.d.ts.map +1 -0
- package/lib/cloudflare/bucket-custom-domain.js +81 -0
- package/lib/cloudflare/bucket-custom-domain.js.map +1 -0
- package/lib/cloudflare/bucket.d.ts +28 -4
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +41 -14
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/cloudflare-env-proxy.d.ts.map +1 -1
- package/lib/cloudflare/cloudflare-env-proxy.js +0 -3
- package/lib/cloudflare/cloudflare-env-proxy.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +3 -1
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.js +73 -9
- package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
- package/lib/cloudflare/react-router/plugin.d.ts +2 -1
- package/lib/cloudflare/react-router/plugin.d.ts.map +1 -1
- package/lib/cloudflare/react-router/plugin.js +6 -14
- package/lib/cloudflare/react-router/plugin.js.map +1 -1
- package/lib/cloudflare/vite/plugin.d.ts.map +1 -1
- package/lib/cloudflare/vite/plugin.js +0 -3
- package/lib/cloudflare/vite/plugin.js.map +1 -1
- package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
- package/lib/cloudflare/worker-bundle.js +17 -0
- package/lib/cloudflare/worker-bundle.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +6 -4
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker-subdomain.d.ts.map +1 -1
- package/lib/cloudflare/worker-subdomain.js +0 -1
- package/lib/cloudflare/worker-subdomain.js.map +1 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +28 -38
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +2 -281
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +23 -11
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/encrypt.d.ts +28 -3
- package/lib/encrypt.d.ts.map +1 -1
- package/lib/encrypt.js +77 -3
- package/lib/encrypt.js.map +1 -1
- package/lib/planetscale/role.d.ts +9 -0
- package/lib/planetscale/role.d.ts.map +1 -1
- package/lib/planetscale/role.js +18 -3
- package/lib/planetscale/role.js.map +1 -1
- package/lib/prisma-postgres/api.d.ts.map +1 -1
- package/lib/prisma-postgres/api.js +1 -0
- package/lib/prisma-postgres/api.js.map +1 -1
- package/lib/scope.d.ts +5 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +23 -0
- package/lib/scope.js.map +1 -1
- package/lib/serde.js +1 -1
- package/lib/serde.js.map +1 -1
- package/lib/util/telemetry.js +4 -0
- package/lib/util/telemetry.js.map +1 -1
- package/package.json +12 -10
- package/src/aws/ec2/index.ts +1 -0
- package/src/aws/index.ts +1 -9
- package/src/cloudflare/auth.ts +1 -0
- package/src/cloudflare/bucket-custom-domain.ts +281 -0
- package/src/cloudflare/bucket.ts +85 -18
- package/src/cloudflare/cloudflare-env-proxy.ts +0 -3
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +3 -1
- package/src/cloudflare/miniflare/remote-binding-proxy.ts +80 -9
- package/src/cloudflare/react-router/plugin.ts +9 -21
- package/src/cloudflare/vite/plugin.ts +0 -3
- package/src/cloudflare/worker-bundle.ts +28 -1
- package/src/cloudflare/worker-metadata.ts +8 -4
- package/src/cloudflare/worker-subdomain.ts +0 -1
- package/src/cloudflare/worker.ts +32 -45
- package/src/cloudflare/wrangler.json.ts +62 -397
- package/src/encrypt.ts +115 -3
- package/src/planetscale/role.ts +30 -3
- package/src/prisma-postgres/api.ts +1 -0
- package/src/scope.ts +27 -0
- package/src/serde.ts +1 -1
- package/src/util/telemetry.ts +5 -0
- package/workers/cloudflare-state-store.js +4802 -8
- package/workers/dofs-state-store.js +632 -2
- package/workers/remote-binding-proxy.js +1929 -3
- package/workers/tunnel-proxy.js +58 -6
- package/workers/tunnel-proxy.ts +1 -1
|
@@ -1,4 +1,432 @@
|
|
|
1
|
-
var B=Object.create;var k=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var F=(s,e)=>(e=Symbol[s])?e:Symbol.for("Symbol."+s),R=s=>{throw TypeError(s)};var $=(s,e,t)=>e in s?k(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var D=(s,e)=>k(s,"name",{value:e,configurable:!0});var U=s=>[,,,B(s?.[F("metadata")]??null)],M=["class","method","getter","setter","accessor","field","value","get","set"],N=s=>s!==void 0&&typeof s!="function"?R("Function expected"):s,V=(s,e,t,r,n)=>({kind:M[s],name:e,metadata:r,addInitializer:o=>t._?R("Already initialized"):n.push(N(o||null))}),J=(s,e)=>$(e,F("metadata"),s[3]),q=(s,e,t,r)=>{for(var n=0,o=s[e>>1],l=o&&o.length;n<l;n++)e&1?o[n].call(t):r=o[n].call(t,r);return r},z=(s,e,t,r,n,o)=>{var l,i,f,u,E,a=e&7,d=!!(e&8),c=!!(e&16),h=a>3?s.length+1:a?d?1:2:0,m=M[a+5],T=a>3&&(s[h-1]=[]),v=s[h]||(s[h]=[]),S=a&&(!c&&!d&&(n=n.prototype),a<5&&(a>3||!c)&&X(a<4?n:{get[t](){return L(this,o)},set[t](g){return P(this,o,g)}},t));a?c&&a<4&&D(o,(a>2?"set ":a>1?"get ":"")+t):D(n,t);for(var O=r.length-1;O>=0;O--)u=V(a,t,f={},s[3],v),a&&(u.static=d,u.private=c,E=u.access={has:c?g=>Y(n,g):g=>t in g},a^3&&(E.get=c?g=>(a^1?L:K)(g,n,a^4?o:S.get):g=>g[t]),a>2&&(E.set=c?(g,x)=>P(g,n,x,a^4?o:S.set):(g,x)=>g[t]=x)),i=(0,r[O])(a?a<4?c?o:S[m]:a>4?void 0:{get:S.get,set:S.set}:n,u),f._=1,a^4||i===void 0?N(i)&&(a>4?T.unshift(i):a?c?o=i:S[m]=i:n=i):typeof i!="object"||i===null?R("Object expected"):(N(l=i.get)&&(S.get=l),N(l=i.set)&&(S.set=l),N(l=i.init)&&T.unshift(l));return a||J(s,n),S&&k(n,t,S),c?a^4?o:S:n};var y=(s,e,t)=>e.has(s)||R("Cannot "+t),Y=(s,e)=>Object(e)!==e?R('Cannot use the "in" operator on this value'):s.has(e),L=(s,e,t)=>(y(s,e,"read from private field"),t?t.call(s):e.get(s));var P=(s,e,t,r)=>(y(s,e,"write to private field"),r?r.call(s,t):e.set(s,t),t),K=(s,e,t)=>(y(s,e,"access private method"),t);import{DurableObject as te,WorkerEntrypoint as se}from"cloudflare:workers";import{DurableObject as G,RpcTarget as Z}from"cloudflare:workers";var A=class extends Z{ctx;env;chunkSize;constructor(s,e,t){super(),this.env=e,this.ctx=s,this.chunkSize=t?.chunkSize??64*1024,this.ctx.blockConcurrencyWhile(async()=>{this.ensureSchema()})}readFile(s,e){let t=this.resolvePathToInode(s),n=this.ctx.storage.sql.exec("SELECT attr FROM dofs_files WHERE ino = ?",t).next().value;if(!n||!n.attr)throw new Error("ENOENT");let l=(typeof n.attr=="string"?JSON.parse(n.attr):n.attr).size||0,i=0,f=this;return new ReadableStream({pull(u){if(console.log("pull",{currentOffset:i,fileSize:l}),i>=l){u.close();return}let E=Math.min(f.chunkSize,l-i),d=f.ctx.storage.sql.exec("SELECT data FROM dofs_chunks WHERE ino = ? AND offset = ? LIMIT 1",t,i).next().value,c;d&&d.data?d.data instanceof ArrayBuffer?c=new Uint8Array(d.data):ArrayBuffer.isView(d.data)?c=new Uint8Array(d.data.buffer):typeof d.data=="string"?c=Uint8Array.from(d.data):c=new Uint8Array(0):c=new Uint8Array(0),console.log("chunk",{chunk:c}),u.enqueue(c),i+=E}})}async writeFile(s,e,t){try{this.unlink(s)}catch(o){if(!(o instanceof Error&&o.message==="ENOENT"))throw o}let r=this.getDeviceSize(),n=this.getSpaceUsed();if(this.create(s),typeof e=="object"&&e!==null&&typeof e.getReader=="function"){let l=0,i=0,f=e.getReader();for(;;){let{value:u,done:E}=await f.read();if(E)break;if(u){if(n+i+u.length>r)throw Object.assign(new Error("ENOSPC"),{code:"ENOSPC"});this.write(s,u,{offset:l,encoding:t?.encoding}),l+=u.length,i+=u.length}}return}if(typeof e=="string"){let o=new TextEncoder().encode(e);if(n+o.length>r)throw Object.assign(new Error("ENOSPC"),{code:"ENOSPC"});this.write(s,o,{offset:0,encoding:t?.encoding});return}if(e instanceof ArrayBuffer){let o=new Uint8Array(e);if(n+o.length>r)throw Object.assign(new Error("ENOSPC"),{code:"ENOSPC"});this.write(s,o,{offset:0,encoding:t?.encoding});return}if(ArrayBuffer.isView(e)){let o=new Uint8Array(e.buffer);if(n+o.length>r)throw Object.assign(new Error("ENOSPC"),{code:"ENOSPC"});this.write(s,o,{offset:0,encoding:t?.encoding});return}throw new Error("Unsupported data type for writeFile")}read(s,e){let t=this.resolvePathToInode(s),r=e?.offset??0,n=e?.length??void 0,o=this.ctx.storage.sql.exec("SELECT offset, data, length FROM dofs_chunks WHERE ino = ? ORDER BY offset ASC",t),l=[],i=0;for(let E of o)if(E.data&&(E.data instanceof ArrayBuffer||ArrayBuffer.isView(E.data))){let a=E.data instanceof ArrayBuffer?new Uint8Array(E.data):new Uint8Array(E.data.buffer);l.push({offset:Number(E.offset),data:a}),i=Math.max(i,Number(E.offset)+a.length)}let f=n!==void 0?r+n:i,u=new Uint8Array(f-r);for(let E of l){let a=E.offset,d=E.offset+E.data.length,c=Math.max(r,a),h=Math.min(f,d);if(c<h){let m=c-r,T=c-a,v=h-c;u.set(E.data.subarray(T,T+v),m)}}return u.buffer}write(s,e,t){let r;try{r=this.resolvePathToInode(s)}catch(T){if(T instanceof Error&&T.message==="ENOENT")this.create(s),r=this.resolvePathToInode(s);else throw T}let n=t?.offset??0,o=typeof e=="string"?new TextEncoder().encode(e):new Uint8Array(e),l=this.getDeviceSize(),i=this.getSpaceUsed(),u=this.ctx.storage.sql.exec("SELECT attr FROM dofs_files WHERE ino = ?",r).next().value,E=0;u&&u.attr&&(E=(typeof u.attr=="string"?JSON.parse(u.attr):u.attr).size||0);let a=n+o.length,d=a>E?a-E:0;if(i+d>l)throw Object.assign(new Error("ENOSPC"),{code:"ENOSPC"});let c=this.chunkSize,h=0,m=0;for(;h<o.length;){let T=n+h,S=Math.floor(T/c)*c,O=T%c,g=Math.min(c-O,o.length-h),x=this.loadChunk(r,S,c);x.set(o.subarray(h,h+g),O);let _=c,b=O+g;b<c&&(_=b),this.ctx.storage.sql.exec("INSERT INTO dofs_chunks (ino, offset, data, length) VALUES (?, ?, ?, ?) ON CONFLICT(ino, offset) DO UPDATE SET data=excluded.data, length=excluded.length",r,S,x.subarray(0,_),_),h+=g,m=Math.max(m,T+g)}this.updateFileSizeAndSpaceUsed(r)}mkdir(s,e){let t=s.split("/").filter(Boolean);if(t.length===0)throw Object.assign(new Error("EEXIST"),{code:"EEXIST"});let r=t[t.length-1],n="/"+t.slice(0,-1).join("/"),o;try{o=this.resolvePathToInode(n)}catch(c){if(c.message==="ENOENT"&&e?.recursive)this.mkdir(n,e),o=this.resolvePathToInode(n);else throw c}if(this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?",o,r).next().value){if(e?.recursive)return;throw Object.assign(new Error("EEXIST"),{code:"EEXIST"})}let i=this.allocInode(),f=Date.now(),u=e?.mode??493,E=e?.umask??0,a=u&~E&4095,d={ino:i,size:0,blocks:0,atime:f,mtime:f,ctime:f,crtime:f,kind:"Directory",perm:a,nlink:2,uid:0,gid:0,rdev:0,flags:0,blksize:512};this.ctx.storage.sql.exec("INSERT INTO dofs_files (ino, name, parent, is_dir, attr, data) VALUES (?, ?, ?, ?, ?, NULL)",i,r,o,1,JSON.stringify(d))}rmdir(s,e){let t;try{t=this.resolvePathToInode(s)}catch(r){if(r.message==="ENOENT"&&e?.recursive)return;throw r}if(e?.recursive){let r=this.ctx.storage.sql.exec("SELECT name, is_dir FROM dofs_files WHERE parent = ?",t);for(let n of r){let o=s==="/"?`/${n.name}`:`${s}/${n.name}`;n.is_dir?this.rmdir(o,e):this.unlink(o)}}else{let n=this.ctx.storage.sql.exec("SELECT COUNT(*) as count FROM dofs_files WHERE parent = ?",t).next().value;if(!n)throw new Error("ENOENT");if(Number(n.count)>0)throw new Error("ENOTEMPTY")}this.ctx.storage.sql.exec("DELETE FROM dofs_files WHERE ino = ?",t)}listDir(s,e){let t=this.resolvePathToInode(s),r=this.ctx.storage.sql.exec("SELECT name, is_dir FROM dofs_files WHERE parent = ?",t),n=[".",".."];for(let o of r)if(typeof o.name=="string"&&(n.push(o.name),e?.recursive&&o.is_dir)){let l=s==="/"?`/${o.name}`:`${s}/${o.name}`,i=this.listDir(l,e);for(let f of i)f!=="."&&f!==".."&&n.push(`${o.name}/${f}`)}return n}stat(s){let e=this.resolvePathToInode(s),r=this.ctx.storage.sql.exec("SELECT attr, is_dir FROM dofs_files WHERE ino = ?",e).next().value;if(!r)throw new Error("ENOENT");let n=typeof r.attr=="string"?JSON.parse(r.attr):r.attr;return{isFile:!r.is_dir,isDirectory:!!r.is_dir,size:n.size,mode:n.perm,uid:n.uid,gid:n.gid,mtime:n.mtime,ctime:n.ctime,atime:n.atime,crtime:n.crtime,blocks:n.blocks,nlink:n.nlink,rdev:n.rdev,flags:n.flags,blksize:n.blksize,kind:n.kind}}setattr(s,e){let t=this.resolvePathToInode(s),n=this.ctx.storage.sql.exec("SELECT attr FROM dofs_files WHERE ino = ?",t).next().value;if(!n)throw new Error("ENOENT");let o=typeof n.attr=="string"?JSON.parse(n.attr):n.attr;e.mode!==void 0&&(o.perm=e.mode),e.uid!==void 0&&(o.uid=e.uid),e.gid!==void 0&&(o.gid=e.gid),this.ctx.storage.sql.exec("UPDATE dofs_files SET attr = ? WHERE ino = ?",JSON.stringify(o),t)}symlink(s,e){let t=e.split("/").filter(Boolean);if(t.length===0)throw new Error("EEXIST");let r=t[t.length-1],n="/"+t.slice(0,-1).join("/"),o=this.resolvePathToInode(n);if(this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?",o,r).next().value)throw new Error("EEXIST");let i=this.allocInode(),f=Date.now(),u={ino:i,size:s.length,blocks:0,atime:f,mtime:f,ctime:f,crtime:f,kind:"Symlink",perm:511,nlink:1,uid:0,gid:0,rdev:0,flags:0,blksize:512},E=new TextEncoder().encode(s);this.ctx.storage.sql.exec("INSERT INTO dofs_files (ino, name, parent, is_dir, attr, data) VALUES (?, ?, ?, ?, ?, ?)",i,r,o,0,JSON.stringify(u),E)}readlink(s){let e=this.resolvePathToInode(s),r=this.ctx.storage.sql.exec("SELECT data FROM dofs_files WHERE ino = ?",e).next().value;if(!r||!r.data)throw new Error("ENOENT");let n;if(r.data instanceof ArrayBuffer)n=new Uint8Array(r.data);else if(ArrayBuffer.isView(r.data))n=new Uint8Array(r.data.buffer);else throw new Error("ENOENT");return new TextDecoder().decode(n)}rename(s,e){let t=s.split("/").filter(Boolean),r=e.split("/").filter(Boolean);if(t.length===0||r.length===0)throw Object.assign(new Error("ENOENT"),{code:"ENOENT"});let n=t[t.length-1],o="/"+t.slice(0,-1).join("/"),l=r[r.length-1],i="/"+r.slice(0,-1).join("/"),f=this.resolvePathToInode(o),u=this.resolvePathToInode(i),a=this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?",f,n).next().value;if(!a)throw Object.assign(new Error("ENOENT"),{code:"ENOENT"});let d=a.ino,h=this.ctx.storage.sql.exec("SELECT ino, is_dir FROM dofs_files WHERE parent = ? AND name = ?",u,l).next().value;if(h){if(h.is_dir){let T=this.ctx.storage.sql.exec("SELECT COUNT(*) as count FROM dofs_files WHERE parent = ?",h.ino).next().value;if(T&&Number(T.count)>0)throw Object.assign(new Error("ENOTEMPTY"),{code:"ENOTEMPTY"})}this.ctx.storage.sql.exec("DELETE FROM dofs_files WHERE ino = ?",h.ino),this.ctx.storage.sql.exec("DELETE FROM dofs_chunks WHERE ino = ?",h.ino)}this.ctx.storage.sql.exec("UPDATE dofs_files SET parent = ?, name = ? WHERE ino = ?",u,l,d)}unlink(s){let e=this.resolvePathToInode(s),r=this.ctx.storage.sql.exec("SELECT is_dir FROM dofs_files WHERE ino = ?",e).next().value;if(!r)throw Object.assign(new Error("ENOENT"),{code:"ENOENT"});if(r.is_dir)throw Object.assign(new Error("EISDIR"),{code:"EISDIR"});this.ctx.storage.sql.exec("DELETE FROM dofs_files WHERE ino = ?",e),this.ctx.storage.sql.exec("DELETE FROM dofs_chunks WHERE ino = ?",e),this.updateFileSizeAndSpaceUsed(e)}create(s,e){let t=s.split("/").filter(Boolean);if(t.length===0)throw Object.assign(new Error("EEXIST"),{code:"EEXIST"});let r=t[t.length-1],n="/"+t.slice(0,-1).join("/"),o=this.resolvePathToInode(n);if(this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?",o,r).next().value)throw Object.assign(new Error("EEXIST"),{code:"EEXIST"});let i=this.allocInode(),f=Date.now(),u=e?.mode??420,E=e?.umask??0,a=u&~E&4095,d={ino:i,size:0,blocks:0,atime:f,mtime:f,ctime:f,crtime:f,kind:"File",perm:a,nlink:1,uid:0,gid:0,rdev:0,flags:0,blksize:512};this.ctx.storage.sql.exec("INSERT INTO dofs_files (ino, name, parent, is_dir, attr, data) VALUES (?, ?, ?, ?, ?, NULL)",i,r,o,0,JSON.stringify(d))}truncate(s,e){let t=this.resolvePathToInode(s),r=this.chunkSize,n=Math.floor(e/r)*r;if(this.ctx.storage.sql.exec("DELETE FROM dofs_chunks WHERE ino = ? AND offset >= ?",t,n),e%r!==0){let o=Math.floor(e/r)*r,l=e%r,i=this.loadChunk(t,o,r);i=i.subarray(0,l),this.ctx.storage.sql.exec("UPDATE dofs_chunks SET data = ?, length = ? WHERE ino = ? AND offset = ?",i,l,t,o)}this.updateFileSizeAndSpaceUsed(t)}getDeviceStats(){let s=this.getDeviceSize(),e=this.getSpaceUsed();return{deviceSize:s,spaceUsed:e,spaceAvailable:s-e}}setDeviceSize(s){let e=this.getSpaceUsed();if(s<e)throw Object.assign(new Error("ENOSPC"),{code:"ENOSPC"});this.ctx.storage.sql.exec("UPDATE dofs_meta SET value = ? WHERE key = ?",s.toString(),"device_size")}rootDirAttr(){let s=Date.now();return{ino:1,size:0,blocks:0,atime:s,mtime:s,ctime:s,crtime:s,kind:"Directory",perm:493,nlink:2,uid:0,gid:0,rdev:0,flags:0,blksize:512}}ensureSchema(){this.ctx.storage.sql.exec(`
|
|
1
|
+
import { DurableObject, RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
|
|
2
|
+
import { timingSafeEqual } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
//#region ../node_modules/.bun/dofs@0.1.0+0530043001070a11/node_modules/dofs/dist/index.js
|
|
5
|
+
var Fs = class extends RpcTarget {
|
|
6
|
+
ctx;
|
|
7
|
+
env;
|
|
8
|
+
chunkSize;
|
|
9
|
+
constructor(ctx, env, options) {
|
|
10
|
+
super();
|
|
11
|
+
this.env = env;
|
|
12
|
+
this.ctx = ctx;
|
|
13
|
+
this.chunkSize = options?.chunkSize ?? 64 * 1024;
|
|
14
|
+
this.ctx.blockConcurrencyWhile(async () => {
|
|
15
|
+
this.ensureSchema();
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
readFile(path, options) {
|
|
19
|
+
const ino = this.resolvePathToInode(path);
|
|
20
|
+
const statRow = this.ctx.storage.sql.exec("SELECT attr FROM dofs_files WHERE ino = ?", ino).next().value;
|
|
21
|
+
if (!statRow || !statRow.attr) throw new Error("ENOENT");
|
|
22
|
+
const fileSize = (typeof statRow.attr === "string" ? JSON.parse(statRow.attr) : statRow.attr).size || 0;
|
|
23
|
+
let currentOffset = 0;
|
|
24
|
+
const self = this;
|
|
25
|
+
return new ReadableStream({ pull(controller) {
|
|
26
|
+
console.log("pull", {
|
|
27
|
+
currentOffset,
|
|
28
|
+
fileSize
|
|
29
|
+
});
|
|
30
|
+
if (currentOffset >= fileSize) {
|
|
31
|
+
controller.close();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const readLength = Math.min(self.chunkSize, fileSize - currentOffset);
|
|
35
|
+
const chunkRow = self.ctx.storage.sql.exec("SELECT data FROM dofs_chunks WHERE ino = ? AND offset = ? LIMIT 1", ino, currentOffset).next().value;
|
|
36
|
+
let chunk;
|
|
37
|
+
if (chunkRow && chunkRow.data) if (chunkRow.data instanceof ArrayBuffer) chunk = new Uint8Array(chunkRow.data);
|
|
38
|
+
else if (ArrayBuffer.isView(chunkRow.data)) chunk = new Uint8Array(chunkRow.data.buffer);
|
|
39
|
+
else if (typeof chunkRow.data === "string") chunk = Uint8Array.from(chunkRow.data);
|
|
40
|
+
else chunk = new Uint8Array(0);
|
|
41
|
+
else chunk = new Uint8Array(0);
|
|
42
|
+
console.log("chunk", { chunk });
|
|
43
|
+
controller.enqueue(chunk);
|
|
44
|
+
currentOffset += readLength;
|
|
45
|
+
} });
|
|
46
|
+
}
|
|
47
|
+
async writeFile(path, data, options) {
|
|
48
|
+
try {
|
|
49
|
+
this.unlink(path);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
if (!(e instanceof Error && e.message === "ENOENT")) throw e;
|
|
52
|
+
}
|
|
53
|
+
const deviceSize = this.getDeviceSize();
|
|
54
|
+
const spaceUsed = this.getSpaceUsed();
|
|
55
|
+
this.create(path);
|
|
56
|
+
if (typeof data === "object" && data !== null && typeof data.getReader === "function") {
|
|
57
|
+
let offset = 0;
|
|
58
|
+
let total = 0;
|
|
59
|
+
const reader = data.getReader();
|
|
60
|
+
while (true) {
|
|
61
|
+
const { value, done } = await reader.read();
|
|
62
|
+
if (done) break;
|
|
63
|
+
if (!value) continue;
|
|
64
|
+
if (spaceUsed + total + value.length > deviceSize) throw Object.assign(/* @__PURE__ */ new Error("ENOSPC"), { code: "ENOSPC" });
|
|
65
|
+
this.write(path, value, {
|
|
66
|
+
offset,
|
|
67
|
+
encoding: options?.encoding
|
|
68
|
+
});
|
|
69
|
+
offset += value.length;
|
|
70
|
+
total += value.length;
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (typeof data === "string") {
|
|
75
|
+
const buf = new TextEncoder().encode(data);
|
|
76
|
+
if (spaceUsed + buf.length > deviceSize) throw Object.assign(/* @__PURE__ */ new Error("ENOSPC"), { code: "ENOSPC" });
|
|
77
|
+
this.write(path, buf, {
|
|
78
|
+
offset: 0,
|
|
79
|
+
encoding: options?.encoding
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (data instanceof ArrayBuffer) {
|
|
84
|
+
const buf = new Uint8Array(data);
|
|
85
|
+
if (spaceUsed + buf.length > deviceSize) throw Object.assign(/* @__PURE__ */ new Error("ENOSPC"), { code: "ENOSPC" });
|
|
86
|
+
this.write(path, buf, {
|
|
87
|
+
offset: 0,
|
|
88
|
+
encoding: options?.encoding
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (ArrayBuffer.isView(data)) {
|
|
93
|
+
const buf = new Uint8Array(data.buffer);
|
|
94
|
+
if (spaceUsed + buf.length > deviceSize) throw Object.assign(/* @__PURE__ */ new Error("ENOSPC"), { code: "ENOSPC" });
|
|
95
|
+
this.write(path, buf, {
|
|
96
|
+
offset: 0,
|
|
97
|
+
encoding: options?.encoding
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
throw new Error("Unsupported data type for writeFile");
|
|
102
|
+
}
|
|
103
|
+
read(path, options) {
|
|
104
|
+
const ino = this.resolvePathToInode(path);
|
|
105
|
+
const offset = options?.offset ?? 0;
|
|
106
|
+
const length = options?.length ?? void 0;
|
|
107
|
+
const cursor = this.ctx.storage.sql.exec("SELECT offset, data, length FROM dofs_chunks WHERE ino = ? ORDER BY offset ASC", ino);
|
|
108
|
+
let chunks = [];
|
|
109
|
+
let fileEnd = 0;
|
|
110
|
+
for (let row of cursor) if (row.data && (row.data instanceof ArrayBuffer || ArrayBuffer.isView(row.data))) {
|
|
111
|
+
const arr = row.data instanceof ArrayBuffer ? new Uint8Array(row.data) : new Uint8Array(row.data.buffer);
|
|
112
|
+
chunks.push({
|
|
113
|
+
offset: Number(row.offset),
|
|
114
|
+
data: arr
|
|
115
|
+
});
|
|
116
|
+
fileEnd = Math.max(fileEnd, Number(row.offset) + arr.length);
|
|
117
|
+
}
|
|
118
|
+
const end = length !== void 0 ? offset + length : fileEnd;
|
|
119
|
+
const result = new Uint8Array(end - offset);
|
|
120
|
+
for (const chunk of chunks) {
|
|
121
|
+
const chunkStart = chunk.offset;
|
|
122
|
+
const chunkEnd = chunk.offset + chunk.data.length;
|
|
123
|
+
const readStart = Math.max(offset, chunkStart);
|
|
124
|
+
const readEnd = Math.min(end, chunkEnd);
|
|
125
|
+
if (readStart < readEnd) {
|
|
126
|
+
const destStart = readStart - offset;
|
|
127
|
+
const srcStart = readStart - chunkStart;
|
|
128
|
+
const len = readEnd - readStart;
|
|
129
|
+
result.set(chunk.data.subarray(srcStart, srcStart + len), destStart);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result.buffer;
|
|
133
|
+
}
|
|
134
|
+
write(path, data, options) {
|
|
135
|
+
let ino;
|
|
136
|
+
try {
|
|
137
|
+
ino = this.resolvePathToInode(path);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
if (e instanceof Error && e.message === "ENOENT") {
|
|
140
|
+
this.create(path);
|
|
141
|
+
ino = this.resolvePathToInode(path);
|
|
142
|
+
} else throw e;
|
|
143
|
+
}
|
|
144
|
+
const offset = options?.offset ?? 0;
|
|
145
|
+
const buf = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
|
|
146
|
+
const deviceSize = this.getDeviceSize();
|
|
147
|
+
const spaceUsed = this.getSpaceUsed();
|
|
148
|
+
const fileRow = this.ctx.storage.sql.exec("SELECT attr FROM dofs_files WHERE ino = ?", ino).next().value;
|
|
149
|
+
let fileSize = 0;
|
|
150
|
+
if (fileRow && fileRow.attr) fileSize = (typeof fileRow.attr === "string" ? JSON.parse(fileRow.attr) : fileRow.attr).size || 0;
|
|
151
|
+
const endOffset = offset + buf.length;
|
|
152
|
+
if (spaceUsed + (endOffset > fileSize ? endOffset - fileSize : 0) > deviceSize) throw Object.assign(/* @__PURE__ */ new Error("ENOSPC"), { code: "ENOSPC" });
|
|
153
|
+
const CHUNK_SIZE = this.chunkSize;
|
|
154
|
+
let written = 0;
|
|
155
|
+
let maxEnd = 0;
|
|
156
|
+
while (written < buf.length) {
|
|
157
|
+
const absOffset = offset + written;
|
|
158
|
+
const chunkOffset = Math.floor(absOffset / CHUNK_SIZE) * CHUNK_SIZE;
|
|
159
|
+
const chunkOffInChunk = absOffset % CHUNK_SIZE;
|
|
160
|
+
const writeLen = Math.min(CHUNK_SIZE - chunkOffInChunk, buf.length - written);
|
|
161
|
+
let chunkData = this.loadChunk(ino, chunkOffset, CHUNK_SIZE);
|
|
162
|
+
chunkData.set(buf.subarray(written, written + writeLen), chunkOffInChunk);
|
|
163
|
+
let chunkLength = CHUNK_SIZE;
|
|
164
|
+
const thisEnd = chunkOffInChunk + writeLen;
|
|
165
|
+
if (thisEnd < CHUNK_SIZE) chunkLength = thisEnd;
|
|
166
|
+
this.ctx.storage.sql.exec("INSERT INTO dofs_chunks (ino, offset, data, length) VALUES (?, ?, ?, ?) ON CONFLICT(ino, offset) DO UPDATE SET data=excluded.data, length=excluded.length", ino, chunkOffset, chunkData.subarray(0, chunkLength), chunkLength);
|
|
167
|
+
written += writeLen;
|
|
168
|
+
maxEnd = Math.max(maxEnd, absOffset + writeLen);
|
|
169
|
+
}
|
|
170
|
+
this.updateFileSizeAndSpaceUsed(ino);
|
|
171
|
+
}
|
|
172
|
+
mkdir(path, options) {
|
|
173
|
+
const parts = path.split("/").filter(Boolean);
|
|
174
|
+
if (parts.length === 0) throw Object.assign(/* @__PURE__ */ new Error("EEXIST"), { code: "EEXIST" });
|
|
175
|
+
const name = parts[parts.length - 1];
|
|
176
|
+
const parentPath = "/" + parts.slice(0, -1).join("/");
|
|
177
|
+
let parent;
|
|
178
|
+
try {
|
|
179
|
+
parent = this.resolvePathToInode(parentPath);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
if (e.message === "ENOENT" && options?.recursive) {
|
|
182
|
+
this.mkdir(parentPath, options);
|
|
183
|
+
parent = this.resolvePathToInode(parentPath);
|
|
184
|
+
} else throw e;
|
|
185
|
+
}
|
|
186
|
+
if (this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?", parent, name).next().value) {
|
|
187
|
+
if (options?.recursive) return;
|
|
188
|
+
throw Object.assign(/* @__PURE__ */ new Error("EEXIST"), { code: "EEXIST" });
|
|
189
|
+
}
|
|
190
|
+
const ino = this.allocInode();
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
const attr = {
|
|
193
|
+
ino,
|
|
194
|
+
size: 0,
|
|
195
|
+
blocks: 0,
|
|
196
|
+
atime: now,
|
|
197
|
+
mtime: now,
|
|
198
|
+
ctime: now,
|
|
199
|
+
crtime: now,
|
|
200
|
+
kind: "Directory",
|
|
201
|
+
perm: (options?.mode ?? 493) & ~(options?.umask ?? 0) & 4095,
|
|
202
|
+
nlink: 2,
|
|
203
|
+
uid: 0,
|
|
204
|
+
gid: 0,
|
|
205
|
+
rdev: 0,
|
|
206
|
+
flags: 0,
|
|
207
|
+
blksize: 512
|
|
208
|
+
};
|
|
209
|
+
this.ctx.storage.sql.exec("INSERT INTO dofs_files (ino, name, parent, is_dir, attr, data) VALUES (?, ?, ?, ?, ?, NULL)", ino, name, parent, 1, JSON.stringify(attr));
|
|
210
|
+
}
|
|
211
|
+
rmdir(path, options) {
|
|
212
|
+
let ino;
|
|
213
|
+
try {
|
|
214
|
+
ino = this.resolvePathToInode(path);
|
|
215
|
+
} catch (e) {
|
|
216
|
+
if (e.message === "ENOENT" && options?.recursive) return;
|
|
217
|
+
throw e;
|
|
218
|
+
}
|
|
219
|
+
if (options?.recursive) {
|
|
220
|
+
const cursor = this.ctx.storage.sql.exec("SELECT name, is_dir FROM dofs_files WHERE parent = ?", ino);
|
|
221
|
+
for (let row of cursor) {
|
|
222
|
+
const childPath = path === "/" ? `/${row.name}` : `${path}/${row.name}`;
|
|
223
|
+
if (row.is_dir) this.rmdir(childPath, options);
|
|
224
|
+
else this.unlink(childPath);
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
const row = this.ctx.storage.sql.exec("SELECT COUNT(*) as count FROM dofs_files WHERE parent = ?", ino).next().value;
|
|
228
|
+
if (!row) throw new Error("ENOENT");
|
|
229
|
+
if (Number(row.count) > 0) throw new Error("ENOTEMPTY");
|
|
230
|
+
}
|
|
231
|
+
this.ctx.storage.sql.exec("DELETE FROM dofs_files WHERE ino = ?", ino);
|
|
232
|
+
}
|
|
233
|
+
listDir(path, options) {
|
|
234
|
+
const ino = this.resolvePathToInode(path);
|
|
235
|
+
const cursor = this.ctx.storage.sql.exec("SELECT name, is_dir FROM dofs_files WHERE parent = ?", ino);
|
|
236
|
+
const names = [".", ".."];
|
|
237
|
+
for (let row of cursor) if (typeof row.name === "string") {
|
|
238
|
+
names.push(row.name);
|
|
239
|
+
if (options?.recursive && row.is_dir) {
|
|
240
|
+
const childPath = path === "/" ? `/${row.name}` : `${path}/${row.name}`;
|
|
241
|
+
const childNames = this.listDir(childPath, options);
|
|
242
|
+
for (const childName of childNames) if (childName !== "." && childName !== "..") names.push(`${row.name}/${childName}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return names;
|
|
246
|
+
}
|
|
247
|
+
stat(path) {
|
|
248
|
+
const ino = this.resolvePathToInode(path);
|
|
249
|
+
const row = this.ctx.storage.sql.exec("SELECT attr, is_dir FROM dofs_files WHERE ino = ?", ino).next().value;
|
|
250
|
+
if (!row) throw new Error("ENOENT");
|
|
251
|
+
const attr = typeof row.attr === "string" ? JSON.parse(row.attr) : row.attr;
|
|
252
|
+
return {
|
|
253
|
+
isFile: !row.is_dir,
|
|
254
|
+
isDirectory: !!row.is_dir,
|
|
255
|
+
size: attr.size,
|
|
256
|
+
mode: attr.perm,
|
|
257
|
+
uid: attr.uid,
|
|
258
|
+
gid: attr.gid,
|
|
259
|
+
mtime: attr.mtime,
|
|
260
|
+
ctime: attr.ctime,
|
|
261
|
+
atime: attr.atime,
|
|
262
|
+
crtime: attr.crtime,
|
|
263
|
+
blocks: attr.blocks,
|
|
264
|
+
nlink: attr.nlink,
|
|
265
|
+
rdev: attr.rdev,
|
|
266
|
+
flags: attr.flags,
|
|
267
|
+
blksize: attr.blksize,
|
|
268
|
+
kind: attr.kind
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
setattr(path, options) {
|
|
272
|
+
const ino = this.resolvePathToInode(path);
|
|
273
|
+
const row = this.ctx.storage.sql.exec("SELECT attr FROM dofs_files WHERE ino = ?", ino).next().value;
|
|
274
|
+
if (!row) throw new Error("ENOENT");
|
|
275
|
+
const attr = typeof row.attr === "string" ? JSON.parse(row.attr) : row.attr;
|
|
276
|
+
if (options.mode !== void 0) attr.perm = options.mode;
|
|
277
|
+
if (options.uid !== void 0) attr.uid = options.uid;
|
|
278
|
+
if (options.gid !== void 0) attr.gid = options.gid;
|
|
279
|
+
this.ctx.storage.sql.exec("UPDATE dofs_files SET attr = ? WHERE ino = ?", JSON.stringify(attr), ino);
|
|
280
|
+
}
|
|
281
|
+
symlink(target, path) {
|
|
282
|
+
const parts = path.split("/").filter(Boolean);
|
|
283
|
+
if (parts.length === 0) throw new Error("EEXIST");
|
|
284
|
+
const name = parts[parts.length - 1];
|
|
285
|
+
const parentPath = "/" + parts.slice(0, -1).join("/");
|
|
286
|
+
const parent = this.resolvePathToInode(parentPath);
|
|
287
|
+
if (this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?", parent, name).next().value) throw new Error("EEXIST");
|
|
288
|
+
const ino = this.allocInode();
|
|
289
|
+
const now = Date.now();
|
|
290
|
+
const attr = {
|
|
291
|
+
ino,
|
|
292
|
+
size: target.length,
|
|
293
|
+
blocks: 0,
|
|
294
|
+
atime: now,
|
|
295
|
+
mtime: now,
|
|
296
|
+
ctime: now,
|
|
297
|
+
crtime: now,
|
|
298
|
+
kind: "Symlink",
|
|
299
|
+
perm: 511,
|
|
300
|
+
nlink: 1,
|
|
301
|
+
uid: 0,
|
|
302
|
+
gid: 0,
|
|
303
|
+
rdev: 0,
|
|
304
|
+
flags: 0,
|
|
305
|
+
blksize: 512
|
|
306
|
+
};
|
|
307
|
+
const data = new TextEncoder().encode(target);
|
|
308
|
+
this.ctx.storage.sql.exec("INSERT INTO dofs_files (ino, name, parent, is_dir, attr, data) VALUES (?, ?, ?, ?, ?, ?)", ino, name, parent, 0, JSON.stringify(attr), data);
|
|
309
|
+
}
|
|
310
|
+
readlink(path) {
|
|
311
|
+
const ino = this.resolvePathToInode(path);
|
|
312
|
+
const row = this.ctx.storage.sql.exec("SELECT data FROM dofs_files WHERE ino = ?", ino).next().value;
|
|
313
|
+
if (!row || !row.data) throw new Error("ENOENT");
|
|
314
|
+
let arr;
|
|
315
|
+
if (row.data instanceof ArrayBuffer) arr = new Uint8Array(row.data);
|
|
316
|
+
else if (ArrayBuffer.isView(row.data)) arr = new Uint8Array(row.data.buffer);
|
|
317
|
+
else throw new Error("ENOENT");
|
|
318
|
+
return new TextDecoder().decode(arr);
|
|
319
|
+
}
|
|
320
|
+
rename(oldPath, newPath) {
|
|
321
|
+
const oldParts = oldPath.split("/").filter(Boolean);
|
|
322
|
+
const newParts = newPath.split("/").filter(Boolean);
|
|
323
|
+
if (oldParts.length === 0 || newParts.length === 0) throw Object.assign(/* @__PURE__ */ new Error("ENOENT"), { code: "ENOENT" });
|
|
324
|
+
const oldName = oldParts[oldParts.length - 1];
|
|
325
|
+
const oldParentPath = "/" + oldParts.slice(0, -1).join("/");
|
|
326
|
+
const newName = newParts[newParts.length - 1];
|
|
327
|
+
const newParentPath = "/" + newParts.slice(0, -1).join("/");
|
|
328
|
+
const oldParent = this.resolvePathToInode(oldParentPath);
|
|
329
|
+
const newParent = this.resolvePathToInode(newParentPath);
|
|
330
|
+
const oldRow = this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?", oldParent, oldName).next().value;
|
|
331
|
+
if (!oldRow) throw Object.assign(/* @__PURE__ */ new Error("ENOENT"), { code: "ENOENT" });
|
|
332
|
+
const ino = oldRow.ino;
|
|
333
|
+
const newRow = this.ctx.storage.sql.exec("SELECT ino, is_dir FROM dofs_files WHERE parent = ? AND name = ?", newParent, newName).next().value;
|
|
334
|
+
if (newRow) {
|
|
335
|
+
if (newRow.is_dir) {
|
|
336
|
+
const childRow = this.ctx.storage.sql.exec("SELECT COUNT(*) as count FROM dofs_files WHERE parent = ?", newRow.ino).next().value;
|
|
337
|
+
if (childRow && Number(childRow.count) > 0) throw Object.assign(/* @__PURE__ */ new Error("ENOTEMPTY"), { code: "ENOTEMPTY" });
|
|
338
|
+
}
|
|
339
|
+
this.ctx.storage.sql.exec("DELETE FROM dofs_files WHERE ino = ?", newRow.ino);
|
|
340
|
+
this.ctx.storage.sql.exec("DELETE FROM dofs_chunks WHERE ino = ?", newRow.ino);
|
|
341
|
+
}
|
|
342
|
+
this.ctx.storage.sql.exec("UPDATE dofs_files SET parent = ?, name = ? WHERE ino = ?", newParent, newName, ino);
|
|
343
|
+
}
|
|
344
|
+
unlink(path) {
|
|
345
|
+
const ino = this.resolvePathToInode(path);
|
|
346
|
+
const row = this.ctx.storage.sql.exec("SELECT is_dir FROM dofs_files WHERE ino = ?", ino).next().value;
|
|
347
|
+
if (!row) throw Object.assign(/* @__PURE__ */ new Error("ENOENT"), { code: "ENOENT" });
|
|
348
|
+
if (row.is_dir) throw Object.assign(/* @__PURE__ */ new Error("EISDIR"), { code: "EISDIR" });
|
|
349
|
+
this.ctx.storage.sql.exec("DELETE FROM dofs_files WHERE ino = ?", ino);
|
|
350
|
+
this.ctx.storage.sql.exec("DELETE FROM dofs_chunks WHERE ino = ?", ino);
|
|
351
|
+
this.updateFileSizeAndSpaceUsed(ino);
|
|
352
|
+
}
|
|
353
|
+
create(path, options) {
|
|
354
|
+
const parts = path.split("/").filter(Boolean);
|
|
355
|
+
if (parts.length === 0) throw Object.assign(/* @__PURE__ */ new Error("EEXIST"), { code: "EEXIST" });
|
|
356
|
+
const name = parts[parts.length - 1];
|
|
357
|
+
const parentPath = "/" + parts.slice(0, -1).join("/");
|
|
358
|
+
const parent = this.resolvePathToInode(parentPath);
|
|
359
|
+
if (this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?", parent, name).next().value) throw Object.assign(/* @__PURE__ */ new Error("EEXIST"), { code: "EEXIST" });
|
|
360
|
+
const ino = this.allocInode();
|
|
361
|
+
const now = Date.now();
|
|
362
|
+
const attr = {
|
|
363
|
+
ino,
|
|
364
|
+
size: 0,
|
|
365
|
+
blocks: 0,
|
|
366
|
+
atime: now,
|
|
367
|
+
mtime: now,
|
|
368
|
+
ctime: now,
|
|
369
|
+
crtime: now,
|
|
370
|
+
kind: "File",
|
|
371
|
+
perm: (options?.mode ?? 420) & ~(options?.umask ?? 0) & 4095,
|
|
372
|
+
nlink: 1,
|
|
373
|
+
uid: 0,
|
|
374
|
+
gid: 0,
|
|
375
|
+
rdev: 0,
|
|
376
|
+
flags: 0,
|
|
377
|
+
blksize: 512
|
|
378
|
+
};
|
|
379
|
+
this.ctx.storage.sql.exec("INSERT INTO dofs_files (ino, name, parent, is_dir, attr, data) VALUES (?, ?, ?, ?, ?, NULL)", ino, name, parent, 0, JSON.stringify(attr));
|
|
380
|
+
}
|
|
381
|
+
truncate(path, size) {
|
|
382
|
+
const ino = this.resolvePathToInode(path);
|
|
383
|
+
const CHUNK_SIZE = this.chunkSize;
|
|
384
|
+
const firstExcessChunk = Math.floor(size / CHUNK_SIZE) * CHUNK_SIZE;
|
|
385
|
+
this.ctx.storage.sql.exec("DELETE FROM dofs_chunks WHERE ino = ? AND offset >= ?", ino, firstExcessChunk);
|
|
386
|
+
if (size % CHUNK_SIZE !== 0) {
|
|
387
|
+
const lastChunkOffset = Math.floor(size / CHUNK_SIZE) * CHUNK_SIZE;
|
|
388
|
+
const lastLen = size % CHUNK_SIZE;
|
|
389
|
+
let chunkData = this.loadChunk(ino, lastChunkOffset, CHUNK_SIZE);
|
|
390
|
+
chunkData = chunkData.subarray(0, lastLen);
|
|
391
|
+
this.ctx.storage.sql.exec("UPDATE dofs_chunks SET data = ?, length = ? WHERE ino = ? AND offset = ?", chunkData, lastLen, ino, lastChunkOffset);
|
|
392
|
+
}
|
|
393
|
+
this.updateFileSizeAndSpaceUsed(ino);
|
|
394
|
+
}
|
|
395
|
+
getDeviceStats() {
|
|
396
|
+
const size = this.getDeviceSize();
|
|
397
|
+
const used = this.getSpaceUsed();
|
|
398
|
+
return {
|
|
399
|
+
deviceSize: size,
|
|
400
|
+
spaceUsed: used,
|
|
401
|
+
spaceAvailable: size - used
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
setDeviceSize(newSize) {
|
|
405
|
+
if (newSize < this.getSpaceUsed()) throw Object.assign(/* @__PURE__ */ new Error("ENOSPC"), { code: "ENOSPC" });
|
|
406
|
+
this.ctx.storage.sql.exec("UPDATE dofs_meta SET value = ? WHERE key = ?", newSize.toString(), "device_size");
|
|
407
|
+
}
|
|
408
|
+
rootDirAttr() {
|
|
409
|
+
const now = Date.now();
|
|
410
|
+
return {
|
|
411
|
+
ino: 1,
|
|
412
|
+
size: 0,
|
|
413
|
+
blocks: 0,
|
|
414
|
+
atime: now,
|
|
415
|
+
mtime: now,
|
|
416
|
+
ctime: now,
|
|
417
|
+
crtime: now,
|
|
418
|
+
kind: "Directory",
|
|
419
|
+
perm: 493,
|
|
420
|
+
nlink: 2,
|
|
421
|
+
uid: 0,
|
|
422
|
+
gid: 0,
|
|
423
|
+
rdev: 0,
|
|
424
|
+
flags: 0,
|
|
425
|
+
blksize: 512
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
ensureSchema() {
|
|
429
|
+
this.ctx.storage.sql.exec(`
|
|
2
430
|
CREATE TABLE IF NOT EXISTS dofs_meta (
|
|
3
431
|
key TEXT PRIMARY KEY,
|
|
4
432
|
value TEXT
|
|
@@ -23,4 +451,206 @@ var B=Object.create;var k=Object.defineProperty;var X=Object.getOwnPropertyDescr
|
|
|
23
451
|
CREATE INDEX IF NOT EXISTS idx_dofs_files_name ON dofs_files(name);
|
|
24
452
|
CREATE INDEX IF NOT EXISTS idx_dofs_chunks_ino ON dofs_chunks(ino);
|
|
25
453
|
CREATE INDEX IF NOT EXISTS idx_dofs_chunks_ino_offset ON dofs_chunks(ino, offset);
|
|
26
|
-
`)
|
|
454
|
+
`);
|
|
455
|
+
if (!this.ctx.storage.sql.exec("SELECT value FROM dofs_meta WHERE key = ?", "device_size").next().value) this.ctx.storage.sql.exec("INSERT INTO dofs_meta (key, value) VALUES (?, ?)", "device_size", (1024 * 1024 * 1024).toString());
|
|
456
|
+
if (!this.ctx.storage.sql.exec("SELECT value FROM dofs_meta WHERE key = ?", "space_used").next().value) this.ctx.storage.sql.exec("INSERT INTO dofs_meta (key, value) VALUES (?, ?)", "space_used", "0");
|
|
457
|
+
const row = this.ctx.storage.sql.exec("SELECT COUNT(*) as count FROM dofs_files WHERE ino = ?", 1).next().value;
|
|
458
|
+
if (!row || row.count === 0) {
|
|
459
|
+
const attr = this.rootDirAttr();
|
|
460
|
+
this.ctx.storage.sql.exec("INSERT INTO dofs_files (ino, name, parent, is_dir, attr, data) VALUES (?, ?, ?, ?, ?, NULL)", 1, "/", void 0, 1, JSON.stringify(attr));
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
resolvePathToInode(path) {
|
|
464
|
+
if (path === "/" || path === "") return 1;
|
|
465
|
+
const parts = path.split("/").filter(Boolean);
|
|
466
|
+
let parent = 1;
|
|
467
|
+
for (const name of parts) {
|
|
468
|
+
const row = this.ctx.storage.sql.exec("SELECT ino FROM dofs_files WHERE parent = ? AND name = ?", parent, name).next().value;
|
|
469
|
+
if (!row || row.ino == null) throw new Error("ENOENT");
|
|
470
|
+
parent = Number(row.ino);
|
|
471
|
+
}
|
|
472
|
+
return parent;
|
|
473
|
+
}
|
|
474
|
+
allocInode() {
|
|
475
|
+
const row = this.ctx.storage.sql.exec("SELECT MAX(ino) as max FROM dofs_files").next().value;
|
|
476
|
+
return row && row.max != null ? Number(row.max) + 1 : 2;
|
|
477
|
+
}
|
|
478
|
+
loadChunk(ino, chunkOffset, chunkSize) {
|
|
479
|
+
const chunkRow = this.ctx.storage.sql.exec("SELECT data FROM dofs_chunks WHERE ino = ? AND offset = ?", ino, chunkOffset).next().value;
|
|
480
|
+
if (chunkRow && chunkRow.data) {
|
|
481
|
+
if (chunkRow.data instanceof ArrayBuffer) return new Uint8Array(chunkRow.data);
|
|
482
|
+
else if (ArrayBuffer.isView(chunkRow.data)) return new Uint8Array(chunkRow.data.buffer);
|
|
483
|
+
}
|
|
484
|
+
return new Uint8Array(chunkSize);
|
|
485
|
+
}
|
|
486
|
+
getDeviceSize() {
|
|
487
|
+
const row = this.ctx.storage.sql.exec("SELECT value FROM dofs_meta WHERE key = ?", "device_size").next().value;
|
|
488
|
+
return row ? Number(row.value) : 1024 * 1024 * 1024;
|
|
489
|
+
}
|
|
490
|
+
getSpaceUsed() {
|
|
491
|
+
const row = this.ctx.storage.sql.exec("SELECT value FROM dofs_meta WHERE key = ?", "space_used").next().value;
|
|
492
|
+
return row ? Number(row.value) : 0;
|
|
493
|
+
}
|
|
494
|
+
setSpaceUsed(val) {
|
|
495
|
+
this.ctx.storage.sql.exec("UPDATE dofs_meta SET value = ? WHERE key = ?", val.toString(), "space_used");
|
|
496
|
+
}
|
|
497
|
+
updateFileSizeAndSpaceUsed(ino) {
|
|
498
|
+
const row = this.ctx.storage.sql.exec("SELECT SUM(length) as total FROM dofs_chunks WHERE ino = ?", ino).next().value;
|
|
499
|
+
const size = row && row.total ? Number(row.total) : 0;
|
|
500
|
+
this.ctx.storage.sql.exec("UPDATE dofs_files SET attr = json_set(attr, \"$.size\", ?) WHERE ino = ?", size, ino);
|
|
501
|
+
const usedRow = this.ctx.storage.sql.exec("SELECT SUM(length) as total FROM dofs_chunks").next().value;
|
|
502
|
+
const used = usedRow && usedRow.total ? Number(usedRow.total) : 0;
|
|
503
|
+
this.setSpaceUsed(used);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
//#endregion
|
|
508
|
+
//#region workers/dofs-state-store.ts
|
|
509
|
+
var dofs_state_store_default = class extends WorkerEntrypoint {
|
|
510
|
+
async fetch(request) {
|
|
511
|
+
if (new URL(request.url).pathname === "/health-check") return new Response("OK", { status: 200 });
|
|
512
|
+
try {
|
|
513
|
+
const body = {
|
|
514
|
+
success: true,
|
|
515
|
+
status: 200,
|
|
516
|
+
result: await this.handle(request) ?? void 0
|
|
517
|
+
};
|
|
518
|
+
return Response.json(body, { status: body.status });
|
|
519
|
+
} catch (error) {
|
|
520
|
+
return APIError.fromUnknown(error).toResponse();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async handle(request) {
|
|
524
|
+
if (!this.authorize(request)) throw new APIError("Unauthorized", 401);
|
|
525
|
+
const stub = this.getStub(request);
|
|
526
|
+
const body = await this.parseBody(request);
|
|
527
|
+
switch (body.method) {
|
|
528
|
+
case "validate": return null;
|
|
529
|
+
case "all": return await stub.all(body.params.prefix);
|
|
530
|
+
case "count": return await stub.count(body.params.prefix);
|
|
531
|
+
case "delete": return await stub.delete(body.params.key);
|
|
532
|
+
case "get": return await stub.get(body.params.key);
|
|
533
|
+
case "getBatch": return await stub.getBatch(body.params.keys);
|
|
534
|
+
case "list": return await stub.list(body.params.prefix);
|
|
535
|
+
case "set": return await stub.set(body.params.key, body.params.value);
|
|
536
|
+
default: throw new APIError("Method not found", 404);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
authorize(request) {
|
|
540
|
+
const token = request.headers.get("Authorization")?.split(" ")[1];
|
|
541
|
+
if (!token) return false;
|
|
542
|
+
const expectedBuffer = Buffer.from(this.env.DOFS_TOKEN, "utf8");
|
|
543
|
+
const providedBuffer = Buffer.alloc(expectedBuffer.length);
|
|
544
|
+
Buffer.from(token, "utf8").copy(providedBuffer, 0, 0, expectedBuffer.length);
|
|
545
|
+
return timingSafeEqual(expectedBuffer, providedBuffer);
|
|
546
|
+
}
|
|
547
|
+
getStub(request) {
|
|
548
|
+
const url = new URL(request.url);
|
|
549
|
+
const app = url.searchParams.get("app");
|
|
550
|
+
const stage = url.searchParams.get("stage");
|
|
551
|
+
if (!app || !stage) throw new APIError("Missing app or stage", 400);
|
|
552
|
+
return this.env.DOFS_STATE_STORE.get(this.env.DOFS_STATE_STORE.idFromName(`${app}/${stage}`));
|
|
553
|
+
}
|
|
554
|
+
async parseBody(request) {
|
|
555
|
+
try {
|
|
556
|
+
return await request.json();
|
|
557
|
+
} catch {
|
|
558
|
+
throw new APIError("Invalid JSON", 400);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
var APIError = class APIError extends Error {
|
|
563
|
+
constructor(message, status) {
|
|
564
|
+
super(message);
|
|
565
|
+
this.status = status;
|
|
566
|
+
}
|
|
567
|
+
toResponse() {
|
|
568
|
+
return Response.json({
|
|
569
|
+
success: false,
|
|
570
|
+
status: this.status,
|
|
571
|
+
error: this.message
|
|
572
|
+
}, { status: this.status });
|
|
573
|
+
}
|
|
574
|
+
static fromUnknown(error) {
|
|
575
|
+
if (error instanceof APIError) return error;
|
|
576
|
+
if (error instanceof Error) return new APIError(error.message, 500);
|
|
577
|
+
return new APIError("An unknown error occurred.", 500);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
var DOFSStateStore = class extends DurableObject {
|
|
581
|
+
fs = new Fs(this.ctx, this.env, { chunkSize: 256 * 1024 });
|
|
582
|
+
async all(prefix) {
|
|
583
|
+
const keys = this.list(prefix);
|
|
584
|
+
const result = {};
|
|
585
|
+
await Promise.all(keys.map(async (key) => {
|
|
586
|
+
const value = await this.get(`${prefix}/${key}`);
|
|
587
|
+
if (value) result[key] = value;
|
|
588
|
+
}));
|
|
589
|
+
return result;
|
|
590
|
+
}
|
|
591
|
+
count(prefix) {
|
|
592
|
+
return this.list(prefix).length;
|
|
593
|
+
}
|
|
594
|
+
delete(key) {
|
|
595
|
+
if (this.isFile(`${key}.json`)) this.fs.unlink(`${key}.json`);
|
|
596
|
+
if (this.isDirectory(key)) this.fs.rmdir(key);
|
|
597
|
+
}
|
|
598
|
+
async get(key) {
|
|
599
|
+
try {
|
|
600
|
+
const file = this.fs.readFile(`${key}.json`);
|
|
601
|
+
return new Response(file).text();
|
|
602
|
+
} catch (error) {
|
|
603
|
+
if (isErrorCode(error, "ENOENT")) return;
|
|
604
|
+
throw error;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async getBatch(keys) {
|
|
608
|
+
const result = {};
|
|
609
|
+
await Promise.all(keys.map(async (key) => {
|
|
610
|
+
const value = await this.get(key);
|
|
611
|
+
if (value) result[key] = value;
|
|
612
|
+
}));
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
list(prefix) {
|
|
616
|
+
const path = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
617
|
+
if (!this.isDirectory(path)) return [];
|
|
618
|
+
return this.fs.listDir(path).filter((item) => item.endsWith(".json")).map((item) => item.slice(0, -5));
|
|
619
|
+
}
|
|
620
|
+
async set(key, value) {
|
|
621
|
+
this.ensureDir(key);
|
|
622
|
+
await this.fs.writeFile(`${key}.json`, JSON.stringify(value));
|
|
623
|
+
}
|
|
624
|
+
ensureDir(path) {
|
|
625
|
+
const dir = path.split("/").slice(0, -1).join("/");
|
|
626
|
+
try {
|
|
627
|
+
this.fs.mkdir(dir, { recursive: true });
|
|
628
|
+
} catch (error) {
|
|
629
|
+
if (isErrorCode(error, "EEXIST")) return;
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
stat(path) {
|
|
634
|
+
try {
|
|
635
|
+
const stat = this.fs.stat(path);
|
|
636
|
+
if (stat.isDirectory) return "directory";
|
|
637
|
+
if (stat.isFile) return "file";
|
|
638
|
+
throw new APIError(`Invalid file type for ${path}`, 500);
|
|
639
|
+
} catch (error) {
|
|
640
|
+
if (isErrorCode(error, "ENOENT")) return "not-found";
|
|
641
|
+
throw error;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
isFile(path) {
|
|
645
|
+
return this.stat(path) === "file";
|
|
646
|
+
}
|
|
647
|
+
isDirectory(path) {
|
|
648
|
+
return this.stat(path) === "directory";
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
const isErrorCode = (error, code) => {
|
|
652
|
+
return error instanceof Error && "message" in error && error.message === code;
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
//#endregion
|
|
656
|
+
export { DOFSStateStore, dofs_state_store_default as default };
|