just-git 1.2.8 → 1.2.9

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 CHANGED
@@ -31,7 +31,7 @@ await bash.exec("echo 'hello' > README.md");
31
31
  await bash.exec("git add . && git commit -m 'initial commit'");
32
32
  ```
33
33
 
34
- Pass a `Git` instance into [just-bash](https://github.com/vercel-labs/just-bash) as a custom command and you get pipes, redirects, `&&` chaining, and the full shell environment alongside git. For standalone use without just-bash, `MemoryFileSystem` provides a minimal in-memory filesystem and `git.exec` accepts a command string with basic quote-aware splitting:
34
+ Pass a `Git` instance into [just-bash](https://github.com/vercel-labs/just-bash) as a custom command and you get pipes, redirects, `&&` chaining, and the full shell environment alongside git. For standalone use without just-bash, `MemoryFileSystem` provides a minimal in-memory filesystem and `git.exec` accepts a single git command string (no shell features):
35
35
 
36
36
  ```ts
37
37
  import { createGit, MemoryFileSystem } from "just-git";
@@ -39,6 +39,7 @@ import { createGit, MemoryFileSystem } from "just-git";
39
39
  const fs = new MemoryFileSystem();
40
40
  const git = createGit({
41
41
  fs,
42
+ cwd: "/repo",
42
43
  identity: { name: "Alice", email: "alice@example.com" },
43
44
  credentials: (url) => ({ type: "bearer", token: process.env.GITHUB_TOKEN! }),
44
45
  hooks: {
@@ -49,11 +50,13 @@ const git = createGit({
49
50
  });
50
51
 
51
52
  await git.exec("git init");
52
- await fs.writeFile("/README.md", "# Hello\n");
53
+ await fs.writeFile("/repo/README.md", "# Hello\n");
53
54
  await git.exec("git add .");
54
55
  await git.exec('git commit -m "initial commit"');
55
56
  ```
56
57
 
58
+ Both `fs` and `cwd` can be set once in `createGit` and overridden per-call. `cwd` defaults to `"/"`. Set it to the repo root so every `exec` call finds `.git` automatically.
59
+
57
60
  `createGit` also supports [command restrictions, network policies, and config overrides](docs/CLIENT.md#options) for sandboxing, a [lifecycle hooks API](docs/CLIENT.md#hooks) covering pre-commit secret scanning to push gating, and [cross-VFS remote resolution](docs/CLIENT.md#multi-agent-collaboration) for multi-agent collaboration. See [CLIENT.md](docs/CLIENT.md) for the full reference.
58
61
 
59
62
  ### Server
@@ -131,16 +134,4 @@ When backed by a real filesystem (e.g. just-bash `ReadWriteFs`), interoperable w
131
134
 
132
135
  ## Examples
133
136
 
134
- Runnable examples in [`examples/`](examples/):
135
-
136
- | File | What it demonstrates |
137
- | --------------------------------------------------------------- | -------------------------------------------------------------------- |
138
- | [`usage.ts`](examples/usage.ts) | Identity, disabled commands, hooks, compose, full sandbox setup |
139
- | [`multi-agent.ts`](examples/multi-agent.ts) | Cross-VFS collaboration with clone/push/pull between isolated agents |
140
- | [`server.ts`](examples/server.ts) | VFS-backed Smart HTTP server with virtual client clone and push |
141
- | [`sqlite-server.ts`](examples/sqlite-server.ts) | SQLite-backed server with auto-creating repos, works with real `git` |
142
- | [`node-server.mjs`](examples/node-server.mjs) | Node.js HTTP server with SQLite + auth via `better-sqlite3` |
143
- | [`platform-server.ts`](examples/platform-server.ts) | GitHub-like PR workflows: create, merge, close via REST API |
144
- | [`agent-remote-workflow.ts`](examples/agent-remote-workflow.ts) | Clone from GitHub, work in sandbox, push back (requires token) |
145
-
146
- Run any example with `bun examples/<file>`.
137
+ Runnable examples in [`examples/`](examples/) — identity, hooks, multi-agent collaboration, Smart HTTP servers, and more. Run any with `bun examples/<file>`.
package/dist/index.d.ts CHANGED
@@ -38,6 +38,12 @@ interface GitOptions {
38
38
  * (just-bash always provides its own filesystem).
39
39
  */
40
40
  fs?: FileSystem;
41
+ /**
42
+ * Default working directory for {@link Git.exec}. Defaults to `"/"`.
43
+ * Per-call `cwd` in {@link ExecContext} overrides this.
44
+ * Ignored by `execute` (just-bash provides its own cwd).
45
+ */
46
+ cwd?: string;
41
47
  hooks?: GitHooks;
42
48
  credentials?: CredentialProvider;
43
49
  identity?: IdentityOverride;
@@ -72,7 +78,7 @@ interface GitOptions {
72
78
  interface ExecContext {
73
79
  /** Filesystem to operate on. Falls back to the `fs` set in {@link GitOptions}. */
74
80
  fs?: FileSystem;
75
- /** Working directory. Defaults to `"/"`. */
81
+ /** Working directory. Falls back to the `cwd` set in {@link GitOptions}, then `"/"`. */
76
82
  cwd?: string;
77
83
  env?: Record<string, string>;
78
84
  stdin?: string;
@@ -92,6 +98,7 @@ interface ExecContext {
92
98
  declare class Git {
93
99
  readonly name = "git";
94
100
  private defaultFs;
101
+ private defaultCwd;
95
102
  private blocked;
96
103
  private hooks;
97
104
  private inner;
package/dist/index.js CHANGED
@@ -375,7 +375,7 @@ Otherwise, please use 'git cherry-pick --skip'
375
375
  ${Le}`,stderr:"",exitCode:0}}})}se();Ne();le();function Vn(e){let t=e.split(".");return t.length===2||t.length===3}function cp(e){let t=[];for(let[n,r]of Object.entries(e)){let o=n.match(/^(\S+)\s+"(.+)"$/);for(let[s,i]of Object.entries(r)){let a=o?`${o[1]}.${o[2]}.${s}`:`${n}.${s}`;for(let c of i)t.push(`${a}=${c}`)}}return t}function dl(e,t){e.command("config",{description:"Get and set repository options",args:[q.string().name("positionals").variadic().optional()],options:{list:I().alias("l").describe("List all config entries"),unset:I().describe("Remove a config key"),"get-all":I().describe("Get all values for a multi-valued key"),add:I().describe("Add a new line without altering existing values")},handler:async(n,r)=>{let o=await W(r.fs,r.cwd,t);if(M(o))return o;let s=o,i=n.positionals,a=i[0];if(n.list)return ll(s);if(n["get-all"]){let f=a;return f?Vn(f)?cl(s,f):L(`error: invalid key: ${f}`,2):L("error: missing key",2)}if(n.add){let f=a,l=i[1];return!f||l===void 0?L("error: missing key and/or value",2):Vn(f)?(await ja(s,f,l),{stdout:"",stderr:"",exitCode:0}):L(`error: invalid key: ${f}`,2)}if(n.unset){let f=a;return f?Vn(f)?fl(s,f):L(`error: invalid key: ${f}`,2):L("error: missing key",2)}if(a==="list")return ll(s);if(a==="get"){let f=i[1];return f?Vn(f)?al(s,f):L(`error: invalid key: ${f}`,2):L("error: missing key",2)}if(a==="get-all"){let f=i[1];return f?Vn(f)?cl(s,f):L(`error: invalid key: ${f}`,2):L("error: missing key",2)}if(a==="set"){let f=i[1],l=i[2];return!f||l===void 0?L("error: missing key and/or value",2):Vn(f)?(await Qo(s,f,l),{stdout:"",stderr:"",exitCode:0}):L(`error: invalid key: ${f}`,2)}if(a==="unset"){let f=i[1];return f?Vn(f)?fl(s,f):L(`error: invalid key: ${f}`,2):L("error: missing key",2)}if(!a)return L("usage: git config [get|set|unset|list] [<key>] [<value>]",2);if(!Vn(a))return L(`error: invalid key: ${a}`,2);let c=i[1];return c!==void 0?(await Qo(s,a,c),{stdout:"",stderr:"",exitCode:0}):al(s,a)}})}async function al(e,t){let n=await ke(e,t);return n===void 0?L(""):{stdout:`${n}
376
376
  `,stderr:"",exitCode:0}}async function cl(e,t){let n=await Ma(e,t);return n.length===0?L(""):{stdout:`${n.join(`
377
377
  `)}
378
- `,stderr:"",exitCode:0}}async function fl(e,t){return await Ga(e,t)?{stdout:"",stderr:"",exitCode:0}:L("",5)}async function ll(e){let t=T(e.gitDir,"config"),n="";await e.fs.exists(t)&&(n=await e.fs.readFile(t));let r=Yo(n),o=cp(r);return{stdout:o.length>0?`${o.join(`
378
+ `,stderr:"",exitCode:0}}async function fl(e,t){return await Ga(e,t)?{stdout:"",stderr:"",exitCode:0}:L("",5)}async function ll(e){let t=T(e.gitDir,"config"),n="";await e.fs.exists(t)&&(n=await e.fs.readFile(t));let r=Yo(n),o=cp(r),s=new Set(o.map(c=>c.split("=")[0])),i=e.configOverrides?.defaults;if(i)for(let[c,f]of Object.entries(i))s.has(c)||(o.push(`${c}=${f}`),s.add(c));let a=e.configOverrides?.locked;if(a)for(let[c,f]of Object.entries(a))s.has(c)||o.push(`${c}=${f}`);return{stdout:o.length>0?`${o.join(`
379
379
  `)}
380
380
  `:"",stderr:"",exitCode:0}}se();function vo(e){let{path:t,parentHashes:n,parentModes:r,resultHash:o,resultMode:s}=e,i=[];i.push(`diff --cc ${t}`);let a=n.map(y=>y?Y(y):"0000000"),c=o?Y(o):"0000000";i.push(`index ${a.join(",")}..${c}`);let f=e.resultContent.length>0,l=n.every(y=>y===null),d=!f&&n.some(y=>y!==null);if(l&&s)i.push(`new file mode ${s}`);else if(d){let y=r.map(x=>x||"000000").join(",");i.push(`deleted file mode ${y}`)}else{let y=r.map(k=>k||"000000"),x=s||"000000";y.some(k=>k!==x)&&i.push(`mode ${y.join(",")}..${x}`)}let m=n.some(y=>y!==null);if(i.push(m?`--- a/${t}`:"--- /dev/null"),i.push(f?`+++ b/${t}`:"+++ /dev/null"),d||l)return`${i.join(`
381
381
  `)}
@@ -759,5 +759,5 @@ ${m}`,exitCode:0}}se();ie();Xr();ue();zs();function dd(e,t){e.command("tag",{des
759
759
  `,stderr:"",exitCode:0}):L(`error: tag '${n.name}' not found.
760
760
  `)}if(n.list)return ld(s,n.name||void 0);if(n.name){if(!Cc(n.name))return $(`'${n.name}' is not a valid tag name`);let i=n.commit,a;if(i){let d=await vt(s,i,`Failed to resolve '${i}' as a valid ref.`);if(M(d))return d;a=d}else if(a=await Z(s),!a)return $("Failed to resolve 'HEAD' as a valid ref.");let c=`refs/tags/${n.name}`;if(await F(s,c)&&!n.force)return $(`tag '${n.name}' already exists`);if(n.annotate||n.message){if(!n.message)return $("no tag message specified (use -m)");let d=await Qe(s,r.env);if(M(d))return d;let m=At(n.message),u=kc({type:"tag",object:a,objectType:"commit",name:n.name,tagger:d,message:m}),h=await Se(s,"tag",u);await X(s,c,h)}else await X(s,c,a);return{stdout:"",stderr:"",exitCode:0}}return ld(s)}})}async function ld(e,t){let n=await ge(e,"refs/tags");if(n.length===0)return{stdout:"",stderr:"",exitCode:0};let r=n.map(o=>o.name.replace("refs/tags/",""));return t&&(r=r.filter(o=>tr(t,o,0)===0)),r.length===0?{stdout:"",stderr:"",exitCode:0}:{stdout:`${r.join(`
761
761
  `)}
762
- `,stderr:"",exitCode:0}}var ow={init:e=>Il(e),clone:(e,t)=>ol(e,t),fetch:(e,t)=>Rl(e,t),pull:(e,t)=>Fl(e,t),push:(e,t)=>Ul(e,t),add:(e,t)=>qc(e,t),blame:(e,t)=>Of(e,t),commit:(e,t)=>il(e,t),status:(e,t)=>id(e,t),log:(e,t)=>Hl(e,t),branch:(e,t)=>If(e,t),tag:(e,t)=>dd(e,t),checkout:(e,t)=>Tf(e,t),diff:(e,t)=>wl(e,t),reset:(e,t)=>zl(e,t),merge:(e,t)=>Ml(e,t),"cherry-pick":(e,t)=>Wf(e,t),revert:(e,t)=>Xl(e,t),rebase:(e,t)=>Nl(e,t),mv:(e,t)=>jl(e,t),rm:(e,t)=>Zl(e,t),remote:(e,t)=>Wl(e,t),config:(e,t)=>dl(e,t),show:(e,t)=>Jl(e,t),stash:(e,t)=>od(e,t),"rev-parse":(e,t)=>Yl(e,t),"ls-files":(e,t)=>Dl(e,t),clean:(e,t)=>qf(e,t),switch:(e,t)=>cd(e,t),restore:(e,t)=>Vl(e,t),reflog:(e,t)=>Bl(e,t),repack:(e,t)=>Cl(e,t),gc:(e,t)=>Ol(e,t),bisect:(e,t)=>uf(e,t)};function ud(e){let t=Ko("git",{description:"Git command"});for(let n of Object.values(ow))n(t,e);return t}var Fo=class{name="git";defaultFs;blocked;hooks;inner;locks=new WeakMap;async withLock(t,n){let r=this.locks.get(t)??Promise.resolve(),o,s=new Promise(i=>{o=i});this.locks.set(t,s),await r;try{return await n()}finally{o()}}constructor(t){this.defaultFs=t?.fs,this.hooks=t?.hooks,this.blocked=t?.disabled?.length?new Set(t.disabled):null;let n=t?.network,r={hooks:t?.hooks,credentialProvider:t?.credentials,identityOverride:t?.identity,fetchFn:typeof n=="object"?n.fetch:void 0,networkPolicy:n,resolveRemote:t?.resolveRemote,...t?.objectStore?{objectStore:t.objectStore}:{},...t?.refStore?{refStore:t.refStore}:{},...t?.config?{configOverrides:t.config}:{}};this.inner=ud(r).toCommand()}exec=async(t,n)=>{let r=n?.fs??this.defaultFs;if(!r)throw new Error("No filesystem: pass `fs` in exec() options or in createGit()");let o=n?.cwd??"/",s=iw(t),i=new Map;if(n?.env)for(let[a,c]of Object.entries(n.env))i.set(a,c);return this.execute(s,{fs:r,cwd:o,env:i,stdin:n?.stdin??""})};execute=(t,n)=>this.withLock(n.fs,async()=>{let r=t[0]??"";if(this.blocked?.has(r))return{stdout:"",stderr:`git: '${r}' is not available in this environment
763
- `,exitCode:1};if(this.hooks?.beforeCommand){let s=await this.hooks.beforeCommand({command:r,args:t.slice(1),fs:n.fs,cwd:n.cwd,env:n.env});if(ee(s))return{stdout:"",stderr:s.message??"",exitCode:1}}let o=await this.inner.execute(t,n);return this.hooks?.afterCommand&&await this.hooks.afterCommand({command:r,args:t.slice(1),result:o}),o})};function iw(e){let t=[],n="",r=0;for(;r<e.length;){let o=e[r];if(o==='"'){for(r++;r<e.length&&e[r]!=='"';){if(e[r]==="\\"&&r+1<e.length){let s=e[r+1];if(s==='"'||s==="\\"){n+=s,r+=2;continue}}n+=e[r],r++}r++}else if(o==="'"){for(r++;r<e.length&&e[r]!=="'";)n+=e[r],r++;r++}else o===" "||o===" "?(n.length>0&&(t.push(n),n=""),r++):(n+=o,r++)}return n.length>0&&t.push(n),t.length>0&&t[0]==="git"&&t.shift(),t}function aw(e){return new Fo(e)}var md=new TextEncoder,cw=new TextDecoder;function ln(e){let t=[];for(let n of e.split("/"))n==="."||n===""||(n===".."?t.pop():t.push(n));return"/"+t.join("/")}function Uo(e){let t=e.lastIndexOf("/");return t<=0?"/":e.slice(0,t)}var Ea=class{data=new Map;constructor(t){if(this.data.set("/",{type:"directory",mode:16877,mtime:new Date}),t)for(let[n,r]of Object.entries(t)){let o=ln(n);this.ensureParents(o),this.data.set(o,{type:"file",content:typeof r=="string"?md.encode(r):r,mode:33188,mtime:new Date})}}ensureParents(t){let n=Uo(t);n!=="/"&&(this.data.has(n)||(this.ensureParents(n),this.data.set(n,{type:"directory",mode:16877,mtime:new Date})))}resolve(t){let n="",r=new Set;for(let o of ln(t).slice(1).split("/")){n=`${n}/${o}`;let s=0,i=this.data.get(n);for(;i?.type==="symlink"&&s<40;){if(r.has(n))throw new Error(`ELOOP: too many levels of symbolic links, '${t}'`);r.add(n);let a=i.target;n=a.startsWith("/")?ln(a):ln(Uo(n)+"/"+a),i=this.data.get(n),s++}if(s>=40)throw new Error(`ELOOP: too many levels of symbolic links, '${t}'`)}return n}resolveParent(t){let n=ln(t);if(n==="/")return"/";let r=n.slice(1).split("/");if(r.length<=1)return n;let o="",s=new Set;for(let i=0;i<r.length-1;i++){o=`${o}/${r[i]}`;let a=this.data.get(o),c=0;for(;a?.type==="symlink"&&c<40;){if(s.has(o))throw new Error(`ELOOP: too many levels of symbolic links, '${t}'`);s.add(o);let f=a.target;o=f.startsWith("/")?ln(f):ln(Uo(o)+"/"+f),a=this.data.get(o),c++}}return`${o}/${r[r.length-1]}`}async readFile(t){return cw.decode(await this.readFileBuffer(t))}async readFileBuffer(t){let n=this.data.get(this.resolve(t));if(!n)throw new Error(`ENOENT: no such file or directory, open '${t}'`);if(n.type!=="file")throw new Error(`EISDIR: illegal operation on a directory, read '${t}'`);return n.content}async writeFile(t,n){let r=this.resolve(t);this.ensureParents(r),this.data.set(r,{type:"file",content:typeof n=="string"?md.encode(n):n,mode:33188,mtime:new Date})}async exists(t){try{return this.data.has(this.resolve(t))}catch{return!1}}async stat(t){let n=this.data.get(this.resolve(t));if(!n)throw new Error(`ENOENT: no such file or directory, stat '${t}'`);return{isFile:n.type==="file",isDirectory:n.type==="directory",isSymbolicLink:!1,mode:n.mode,size:n.type==="file"?n.content.byteLength:0,mtime:n.mtime}}async lstat(t){let n=this.resolveParent(t),r=this.data.get(n);if(!r)throw new Error(`ENOENT: no such file or directory, lstat '${t}'`);return{isFile:r.type==="file",isDirectory:r.type==="directory",isSymbolicLink:r.type==="symlink",mode:r.mode,size:r.type==="file"?r.content.byteLength:r.type==="symlink"?r.target.length:0,mtime:r.mtime}}async mkdir(t,n){let r=ln(t);if(this.data.has(r)){if(this.data.get(r).type!=="directory")throw new Error(`EEXIST: file already exists, mkdir '${t}'`);if(!n?.recursive)throw new Error(`EEXIST: directory already exists, mkdir '${t}'`);return}let o=Uo(r);if(o!=="/"&&!this.data.has(o))if(n?.recursive)await this.mkdir(o,{recursive:!0});else throw new Error(`ENOENT: no such file or directory, mkdir '${t}'`);this.data.set(r,{type:"directory",mode:16877,mtime:new Date})}async readdir(t){let n=this.resolve(t),r=this.data.get(n);if(!r)throw new Error(`ENOENT: no such file or directory, scandir '${t}'`);if(r.type!=="directory")throw new Error(`ENOTDIR: not a directory, scandir '${t}'`);let o=n==="/"?"/":`${n}/`,s=new Set;for(let i of this.data.keys())if(i!==n&&i.startsWith(o)){let c=i.slice(o.length).split("/")[0];c&&s.add(c)}return[...s].sort()}async rm(t,n){let r=ln(t),o=this.data.get(r);if(!o){if(n?.force)return;throw new Error(`ENOENT: no such file or directory, rm '${t}'`)}if(o.type==="directory"){if(!n?.recursive&&(await this.readdir(r)).length>0)throw new Error(`ENOTEMPTY: directory not empty, rm '${t}'`);let s=r==="/"?"/":`${r}/`;for(let i of[...this.data.keys()])i.startsWith(s)&&this.data.delete(i)}this.data.delete(r)}async readlink(t){let n=this.resolveParent(t),r=this.data.get(n);if(!r)throw new Error(`ENOENT: no such file or directory, readlink '${t}'`);if(r.type!=="symlink")throw new Error(`EINVAL: invalid argument, readlink '${t}'`);return r.target}async symlink(t,n){let r=ln(n);if(this.data.has(r))throw new Error(`EEXIST: file already exists, symlink '${n}'`);this.ensureParents(r),this.data.set(r,{type:"symlink",target:t,mode:40960,mtime:new Date})}};Hn();export{Fo as Git,Ea as MemoryFileSystem,gh as composeGitHooks,aw as createGit,Qn as findRepo,ee as isRejection};
762
+ `,stderr:"",exitCode:0}}var ow={init:e=>Il(e),clone:(e,t)=>ol(e,t),fetch:(e,t)=>Rl(e,t),pull:(e,t)=>Fl(e,t),push:(e,t)=>Ul(e,t),add:(e,t)=>qc(e,t),blame:(e,t)=>Of(e,t),commit:(e,t)=>il(e,t),status:(e,t)=>id(e,t),log:(e,t)=>Hl(e,t),branch:(e,t)=>If(e,t),tag:(e,t)=>dd(e,t),checkout:(e,t)=>Tf(e,t),diff:(e,t)=>wl(e,t),reset:(e,t)=>zl(e,t),merge:(e,t)=>Ml(e,t),"cherry-pick":(e,t)=>Wf(e,t),revert:(e,t)=>Xl(e,t),rebase:(e,t)=>Nl(e,t),mv:(e,t)=>jl(e,t),rm:(e,t)=>Zl(e,t),remote:(e,t)=>Wl(e,t),config:(e,t)=>dl(e,t),show:(e,t)=>Jl(e,t),stash:(e,t)=>od(e,t),"rev-parse":(e,t)=>Yl(e,t),"ls-files":(e,t)=>Dl(e,t),clean:(e,t)=>qf(e,t),switch:(e,t)=>cd(e,t),restore:(e,t)=>Vl(e,t),reflog:(e,t)=>Bl(e,t),repack:(e,t)=>Cl(e,t),gc:(e,t)=>Ol(e,t),bisect:(e,t)=>uf(e,t)};function ud(e){let t=Ko("git",{description:"Git command"});for(let n of Object.values(ow))n(t,e);return t}function iw(e,t){if(!e)return t;let n=e.locked?"locked":"defaults",r={"user.name":e.name,"user.email":e.email};return t?{...t,[n]:{...r,...t[n]}}:{[n]:r}}var Fo=class{name="git";defaultFs;defaultCwd;blocked;hooks;inner;locks=new WeakMap;async withLock(t,n){let r=this.locks.get(t)??Promise.resolve(),o,s=new Promise(i=>{o=i});this.locks.set(t,s),await r;try{return await n()}finally{o()}}constructor(t){this.defaultFs=t?.fs,this.defaultCwd=t?.cwd??"/",this.hooks=t?.hooks,this.blocked=t?.disabled?.length?new Set(t.disabled):null;let n=t?.network,r=iw(t?.identity,t?.config),o={hooks:t?.hooks,credentialProvider:t?.credentials,identityOverride:t?.identity,fetchFn:typeof n=="object"?n.fetch:void 0,networkPolicy:n,resolveRemote:t?.resolveRemote,...t?.objectStore?{objectStore:t.objectStore}:{},...t?.refStore?{refStore:t.refStore}:{},...r?{configOverrides:r}:{}};this.inner=ud(o).toCommand()}exec=async(t,n)=>{let r=n?.fs??this.defaultFs;if(!r)throw new Error("No filesystem: pass `fs` in exec() options or in createGit()");let o=n?.cwd??this.defaultCwd,s=aw(t),i=new Map;if(n?.env)for(let[a,c]of Object.entries(n.env))i.set(a,c);return this.execute(s,{fs:r,cwd:o,env:i,stdin:n?.stdin??""})};execute=(t,n)=>this.withLock(n.fs,async()=>{let r=t[0]??"";if(this.blocked?.has(r))return{stdout:"",stderr:`git: '${r}' is not available in this environment
763
+ `,exitCode:1};if(this.hooks?.beforeCommand){let s=await this.hooks.beforeCommand({command:r,args:t.slice(1),fs:n.fs,cwd:n.cwd,env:n.env});if(ee(s))return{stdout:"",stderr:s.message??"",exitCode:1}}let o=await this.inner.execute(t,n);return this.hooks?.afterCommand&&await this.hooks.afterCommand({command:r,args:t.slice(1),result:o}),o})};function aw(e){let t=[],n="",r=0;for(;r<e.length;){let o=e[r];if(o==='"'){for(r++;r<e.length&&e[r]!=='"';){if(e[r]==="\\"&&r+1<e.length){let s=e[r+1];if(s==='"'||s==="\\"){n+=s,r+=2;continue}}n+=e[r],r++}r++}else if(o==="'"){for(r++;r<e.length&&e[r]!=="'";)n+=e[r],r++;r++}else o===" "||o===" "?(n.length>0&&(t.push(n),n=""),r++):(n+=o,r++)}return n.length>0&&t.push(n),t.length>0&&t[0]==="git"&&t.shift(),t}function cw(e){return new Fo(e)}var md=new TextEncoder,fw=new TextDecoder;function ln(e){let t=[];for(let n of e.split("/"))n==="."||n===""||(n===".."?t.pop():t.push(n));return"/"+t.join("/")}function Uo(e){let t=e.lastIndexOf("/");return t<=0?"/":e.slice(0,t)}var Ea=class{data=new Map;constructor(t){if(this.data.set("/",{type:"directory",mode:16877,mtime:new Date}),t)for(let[n,r]of Object.entries(t)){let o=ln(n);this.ensureParents(o),this.data.set(o,{type:"file",content:typeof r=="string"?md.encode(r):r,mode:33188,mtime:new Date})}}ensureParents(t){let n=Uo(t);n!=="/"&&(this.data.has(n)||(this.ensureParents(n),this.data.set(n,{type:"directory",mode:16877,mtime:new Date})))}resolve(t){let n="",r=new Set;for(let o of ln(t).slice(1).split("/")){n=`${n}/${o}`;let s=0,i=this.data.get(n);for(;i?.type==="symlink"&&s<40;){if(r.has(n))throw new Error(`ELOOP: too many levels of symbolic links, '${t}'`);r.add(n);let a=i.target;n=a.startsWith("/")?ln(a):ln(Uo(n)+"/"+a),i=this.data.get(n),s++}if(s>=40)throw new Error(`ELOOP: too many levels of symbolic links, '${t}'`)}return n}resolveParent(t){let n=ln(t);if(n==="/")return"/";let r=n.slice(1).split("/");if(r.length<=1)return n;let o="",s=new Set;for(let i=0;i<r.length-1;i++){o=`${o}/${r[i]}`;let a=this.data.get(o),c=0;for(;a?.type==="symlink"&&c<40;){if(s.has(o))throw new Error(`ELOOP: too many levels of symbolic links, '${t}'`);s.add(o);let f=a.target;o=f.startsWith("/")?ln(f):ln(Uo(o)+"/"+f),a=this.data.get(o),c++}}return`${o}/${r[r.length-1]}`}async readFile(t){return fw.decode(await this.readFileBuffer(t))}async readFileBuffer(t){let n=this.data.get(this.resolve(t));if(!n)throw new Error(`ENOENT: no such file or directory, open '${t}'`);if(n.type!=="file")throw new Error(`EISDIR: illegal operation on a directory, read '${t}'`);return n.content}async writeFile(t,n){let r=this.resolve(t);this.ensureParents(r),this.data.set(r,{type:"file",content:typeof n=="string"?md.encode(n):n,mode:33188,mtime:new Date})}async exists(t){try{return this.data.has(this.resolve(t))}catch{return!1}}async stat(t){let n=this.data.get(this.resolve(t));if(!n)throw new Error(`ENOENT: no such file or directory, stat '${t}'`);return{isFile:n.type==="file",isDirectory:n.type==="directory",isSymbolicLink:!1,mode:n.mode,size:n.type==="file"?n.content.byteLength:0,mtime:n.mtime}}async lstat(t){let n=this.resolveParent(t),r=this.data.get(n);if(!r)throw new Error(`ENOENT: no such file or directory, lstat '${t}'`);return{isFile:r.type==="file",isDirectory:r.type==="directory",isSymbolicLink:r.type==="symlink",mode:r.mode,size:r.type==="file"?r.content.byteLength:r.type==="symlink"?r.target.length:0,mtime:r.mtime}}async mkdir(t,n){let r=ln(t);if(this.data.has(r)){if(this.data.get(r).type!=="directory")throw new Error(`EEXIST: file already exists, mkdir '${t}'`);if(!n?.recursive)throw new Error(`EEXIST: directory already exists, mkdir '${t}'`);return}let o=Uo(r);if(o!=="/"&&!this.data.has(o))if(n?.recursive)await this.mkdir(o,{recursive:!0});else throw new Error(`ENOENT: no such file or directory, mkdir '${t}'`);this.data.set(r,{type:"directory",mode:16877,mtime:new Date})}async readdir(t){let n=this.resolve(t),r=this.data.get(n);if(!r)throw new Error(`ENOENT: no such file or directory, scandir '${t}'`);if(r.type!=="directory")throw new Error(`ENOTDIR: not a directory, scandir '${t}'`);let o=n==="/"?"/":`${n}/`,s=new Set;for(let i of this.data.keys())if(i!==n&&i.startsWith(o)){let c=i.slice(o.length).split("/")[0];c&&s.add(c)}return[...s].sort()}async rm(t,n){let r=ln(t),o=this.data.get(r);if(!o){if(n?.force)return;throw new Error(`ENOENT: no such file or directory, rm '${t}'`)}if(o.type==="directory"){if(!n?.recursive&&(await this.readdir(r)).length>0)throw new Error(`ENOTEMPTY: directory not empty, rm '${t}'`);let s=r==="/"?"/":`${r}/`;for(let i of[...this.data.keys()])i.startsWith(s)&&this.data.delete(i)}this.data.delete(r)}async readlink(t){let n=this.resolveParent(t),r=this.data.get(n);if(!r)throw new Error(`ENOENT: no such file or directory, readlink '${t}'`);if(r.type!=="symlink")throw new Error(`EINVAL: invalid argument, readlink '${t}'`);return r.target}async symlink(t,n){let r=ln(n);if(this.data.has(r))throw new Error(`EEXIST: file already exists, symlink '${n}'`);this.ensureParents(r),this.data.set(r,{type:"symlink",target:t,mode:40960,mtime:new Date})}};Hn();export{Fo as Git,Ea as MemoryFileSystem,gh as composeGitHooks,cw as createGit,Qn as findRepo,ee as isRejection};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "just-git",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "description": "Pure TypeScript git implementation: virtual filesystem client and embeddable server.",
5
5
  "keywords": [
6
6
  "agent",